summaryrefslogtreecommitdiff
path: root/core/gjs/src/variable.ts
diff options
context:
space:
mode:
Diffstat (limited to 'core/gjs/src/variable.ts')
-rw-r--r--core/gjs/src/variable.ts231
1 files changed, 0 insertions, 231 deletions
diff --git a/core/gjs/src/variable.ts b/core/gjs/src/variable.ts
deleted file mode 100644
index 8739ef6..0000000
--- a/core/gjs/src/variable.ts
+++ /dev/null
@@ -1,231 +0,0 @@
-import Binding, { type Connectable } from "./binding.js"
-import { Astal } from "./imports.js"
-import { interval, idle } from "./time.js"
-import { execAsync, subprocess } from "./process.js"
-
-class VariableWrapper<T> extends Function {
- private variable!: Astal.VariableBase
- private errHandler? = console.error
-
- private _value: T
- private _poll?: Astal.Time
- private _watch?: Astal.Process
-
- private pollInterval = 1000
- private pollExec?: string[] | string
- private pollTransform?: (stdout: string, prev: T) => T
- private pollFn?: (prev: T) => T | Promise<T>
-
- private watchTransform?: (stdout: string, prev: T) => T
- private watchExec?: string[] | string
-
- constructor(init: T) {
- super()
- this._value = init
- this.variable = new Astal.VariableBase()
- this.variable.connect("dropped", () => {
- this.stopWatch()
- this.stopPoll()
- })
- this.variable.connect("error", (_, err) => this.errHandler?.(err))
- return new Proxy(this, {
- apply: (target, _, args) => target._call(args[0]),
- })
- }
-
- private _call<R = T>(transform?: (value: T) => R): Binding<R> {
- const b = Binding.bind(this)
- return transform ? b.as(transform) : b as unknown as Binding<R>
- }
-
- toString() {
- return String(`Variable<${this.get()}>`)
- }
-
- get(): T { return this._value }
- set(value: T) {
- if (value !== this._value) {
- this._value = value
- this.variable.emit("changed")
- }
- }
-
- startPoll() {
- if (this._poll)
- return
-
- if (this.pollFn) {
- this._poll = interval(this.pollInterval, () => {
- const v = this.pollFn!(this.get())
- if (v instanceof Promise) {
- v.then(v => this.set(v))
- .catch(err => this.variable.emit("error", err))
- }
- else {
- this.set(v)
- }
- })
- }
- else if (this.pollExec) {
- this._poll = interval(this.pollInterval, () => {
- execAsync(this.pollExec!)
- .then(v => this.set(this.pollTransform!(v, this.get())))
- .catch(err => this.variable.emit("error", err))
- })
- }
- }
-
- startWatch() {
- if (this._watch)
- return
-
- this._watch = subprocess({
- cmd: this.watchExec!,
- out: out => this.set(this.watchTransform!(out, this.get())),
- err: err => this.variable.emit("error", err),
- })
- }
-
- stopPoll() {
- this._poll?.cancel()
- delete this._poll
- }
-
- stopWatch() {
- this._watch?.kill()
- delete this._watch
- }
-
- isPolling() { return !!this._poll }
- isWatching() { return !!this._watch }
-
- drop() {
- this.variable.emit("dropped")
- }
-
- onDropped(callback: () => void) {
- this.variable.connect("dropped", callback)
- return this as unknown as Variable<T>
- }
-
- onError(callback: (err: string) => void) {
- delete this.errHandler
- this.variable.connect("error", (_, err) => callback(err))
- return this as unknown as Variable<T>
- }
-
- subscribe(callback: (value: T) => void) {
- const id = this.variable.connect("changed", () => {
- callback(this.get())
- })
- return () => this.variable.disconnect(id)
- }
-
- poll(
- interval: number,
- exec: string | string[],
- transform?: (stdout: string, prev: T) => T
- ): Variable<T>
-
- poll(
- interval: number,
- callback: (prev: T) => T | Promise<T>
- ): Variable<T>
-
- poll(
- interval: number,
- exec: string | string[] | ((prev: T) => T | Promise<T>),
- transform: (stdout: string, prev: T) => T = out => out as T,
- ) {
- this.stopPoll()
- this.pollInterval = interval
- this.pollTransform = transform
- if (typeof exec === "function") {
- this.pollFn = exec
- delete this.pollExec
- }
- else {
- this.pollExec = exec
- delete this.pollFn
- }
- this.startPoll()
- return this as unknown as Variable<T>
- }
-
- watch(
- exec: string | string[],
- transform: (stdout: string, prev: T) => T = out => out as T,
- ) {
- this.stopWatch()
- this.watchExec = exec
- this.watchTransform = transform
- this.startWatch()
- return this as unknown as Variable<T>
- }
-
- observe(
- objs: Array<[obj: Connectable, signal: string]>,
- callback: (...args: any[]) => T,
- ): Variable<T>
-
- observe(
- obj: Connectable,
- signal: string,
- callback: (...args: any[]) => T,
- ): Variable<T>
-
- observe(
- objs: Connectable | Array<[obj: Connectable, signal: string]>,
- sigOrFn: string | ((obj: Connectable, ...args: any[]) => T),
- callback?: (obj: Connectable, ...args: any[]) => T,
- ) {
- const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => this.get())
- const set = (obj: Connectable, ...args: any[]) => this.set(f(obj, ...args))
-
- if (Array.isArray(objs)) {
- for (const obj of objs) {
- const [o, s] = obj
- const id = o.connect(s, set)
- this.onDropped(() => o.disconnect(id))
- }
- }
- else {
- if (typeof sigOrFn === "string") {
- const id = objs.connect(sigOrFn, set)
- this.onDropped(() => objs.disconnect(id))
- }
- }
-
- return this as unknown as Variable<T>
- }
-
- static derive<
- const Deps extends Array<Variable<any> | Binding<any>>,
- Args extends {
- [K in keyof Deps]: Deps[K] extends Variable<infer T>
- ? T : Deps[K] extends Binding<infer T> ? T : never
- },
- V = Args,
- >(deps: Deps, fn: (...args: Args) => V = (...args) => args as unknown as V) {
- const update = () => fn(...deps.map(d => d.get()) as Args)
- const derived = new Variable(update())
- const unsubs = deps.map(dep => dep.subscribe(() => derived.set(update())))
- derived.onDropped(() => unsubs.map(unsub => unsub()))
- return derived
- }
-}
-
-export interface Variable<T> extends Omit<VariableWrapper<T>, "bind"> {
- <R>(transform: (value: T) => R): Binding<R>
- (): Binding<T>
-}
-
-export const Variable = new Proxy(VariableWrapper as any, {
- apply: (_t, _a, args) => new VariableWrapper(args[0]),
-}) as {
- derive: typeof VariableWrapper["derive"]
- <T>(init: T): Variable<T>
- new <T>(init: T): Variable<T>
-}
-
-export default Variable