about summary refs log tree commit diff
path: root/users/Profpatsch
diff options
context:
space:
mode:
authorProfpatsch <mail@profpatsch.de>2024-12-10T02·16+0100
committerProfpatsch <mail@profpatsch.de>2024-12-10T15·34+0000
commitc12fdeb8e003685fc7fe9f3c579afefac30a054b (patch)
tree4dd72e032f6f972c1eff2fbf8df2fefd1df72844 /users/Profpatsch
parent4eeac3cb1dcf933482d00169140a09a7c01f3a3f (diff)
refactor(users/Profpatsch): simple wrapper around the dbus lib r/8999
This makes defining the interface a little less verbose & more
typesafe (checks are done by the dbus library).

Change-Id: I16df987fd152cabf76ed9878ed1a372a0f7003fb
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12886
Reviewed-by: Profpatsch <mail@profpatsch.de>
Tested-by: BuildkiteCI
Diffstat (limited to 'users/Profpatsch')
-rw-r--r--users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js246
1 files changed, 159 insertions, 87 deletions
diff --git a/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js b/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js
index ca2e86a2b798..728463e407da 100644
--- a/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js
+++ b/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js
@@ -35,9 +35,113 @@ const lightTheme = getThemePathSync(lightThemeName);
 console.log(`Dark theme: ${darkTheme}`);
 console.log(`Light theme: ${lightTheme}`);
 
+class Bus {
+  /**
+   * @param {'session' | 'system'} type
+   * @param {string} name
+   */
+  constructor(name, type) {
+    this._name = name;
+    this._type = type;
+    switch (type) {
+      case 'session':
+        this._bus = dbus.sessionBus();
+        break;
+      case 'system':
+        this._bus = dbus.systemBus();
+        break;
+    }
+    this._bus.connection.once('error', err => {
+      console.error(`${this._type} bus ${this._name} error: ${err}`);
+      throw new Error(`${this._type} bus ${this._name} error: ${err}`);
+    });
+    this._bus.connection.once('end', () => {
+      console.error(`${this._type} bus ${this._name} connection ended unexpectedly`);
+      throw new Error(`${this._type} bus ${this._name} connection ended unexpectedly`);
+    });
+  }
+
+  _busErrorMessages(what) {
+    return `Error getting ${what} from ${this._type} bus ${this._name}`;
+  }
+
+  /**
+   *
+   * @param {string} name
+   * @param {number} flags
+   * @returns {Promise<number>}
+   */
+  requestName(name, flags) {
+    return promisifyMethodAnnotate(
+      this._bus,
+      // @ts-ignore
+      this._bus.requestName,
+      this._busErrorMessages(`requesting name ${name}`),
+      name,
+      flags,
+    );
+  }
+
+  /**
+   * @param {{ [key: string]: unknown }} iface
+   * @param {string} path
+   * @param {{ name: string; methods: { [key: string]: unknown }; }} opts
+   */
+  exportInterface(iface, path, opts) {
+    // @ts-ignore
+    return this._bus.exportInterface(iface, path, opts);
+  }
+
+  /**
+  /** Get object from bus, with the given interface (not checked!)
+   * @template {{[key: string]: (...args: any[]) => Promise<unknown>}} Interface
+   * @param {string} serviceName
+   * @param {string} interfaceName
+   * @param {string} objectName object name
+   * @returns {Promise<Interface & EventEmitter >}
+   */
+  async getObject(serviceName, interfaceName, objectName) {
+    //@ts-ignore
+    const s = this._bus.getService(serviceName);
+
+    /** @type {{[key: string]: Function}} */
+    // @ts-ignore
+    const iface = await promisifyMethodAnnotate(
+      s,
+      s.getInterface,
+      this._busErrorMessages(`interface ${interfaceName}`),
+      objectName,
+      interfaceName,
+    );
+
+    if (!iface) {
+      throw new Error(
+        `Interface ${interfaceName} not found on object ${objectName} of service ${serviceName}`,
+      );
+    }
+
+    // We need to promisify all methods on the interface
+    const methodNames = Object.keys(iface.$methods ?? {});
+    const methods = {};
+    /** @type {Record<string, Function>} */
+    for (const methodName of methodNames) {
+      methods[methodName] = iface[methodName];
+      iface[methodName] = (...args) =>
+        promisifyMethodAnnotate(
+          iface,
+          methods[methodName],
+          this._busErrorMessages(`method ${methodName}`),
+          ...args,
+        );
+    }
+
+    // @ts-ignore
+    return iface;
+  }
+}
+
 // Connect to the user session bus
-/** @type any */
-const bus = dbus.sessionBus();
+const bus = new Bus('color-scheme', 'session');
 
 opentelemetry.diag.setLogger({ ...console, verbose: console.log });
 
@@ -167,17 +271,6 @@ function setupActiveSpan(tracer, spanData) {
   );
 }
 
-// tracer.startActiveSpan('222', span => {
-//   span.setAttribute('blabla', 'alacritty-change-color-scheme');
-//   tracer.startActiveSpan('333', span => {
-//     span.setAttribute('foo', 'bar');
-//     span.end();
-//   });
-//   // span.setAttribute('service.name', 'alacritty-change-color-scheme');
-//   // span.setAttribute('service.version', '0.0.1');
-//   span.end();
-// });
-
 // set XDG_CONFIG_HOME if it's not set
 if (!process.env.XDG_CONFIG_HOME) {
   process.env.XDG_CONFIG_HOME = process.env.HOME + '/.config';
@@ -196,7 +289,8 @@ function getThemePathSync(theme) {
  */
 function writeAlacrittyColorConfig(cs) {
   const theme = cs === 'prefer-dark' ? darkTheme : lightTheme;
-  console.log(`Writing color scheme ${cs} with theme ${theme}`);
+  console.log(`
+    Writing color scheme ${cs} with theme ${theme}`);
   fs.writeFileSync(
     process.env.XDG_CONFIG_HOME + '/alacritty/alacritty-colors-autogen.toml',
     `# !! THIS FILE IS GENERATED BY ${programName}
@@ -205,27 +299,35 @@ general.import = ["${theme}"]`,
   );
 }
 
+/** Typescript type that returns the inner value type T from a Promise<T>
+ * type PromiseVal<T> = T extends Promise<infer U> ? U : T;
+ * @template T
+ * @typedef {T extends Promise<infer U> ? U : T } PromiseVal
+ */
+
+/**
+ * @template {{[key: string]: (...args: any[]) => Promise<unknown>}} T
+ * @typedef {typeof Bus.prototype.getObject<T>} GetObject<T>
+ */
+
+/**
+ * @template {{[key: string]: (...args: any[]) => Promise<unknown>}} T
+ * @typedef {PromiseVal<ReturnType<GetObject<T>>>} IfaceReturn<T> */
+
 /** get the current value of the color scheme from dbus
  *
  * @returns {Promise<'prefer-dark' | 'prefer-light'>}
  */
 async function getColorScheme() {
-  let service = bus.getService('org.freedesktop.portal.Desktop');
-  let iface = await promisifyMethodAnnotate(
-    service,
-    service.getInterface,
-    'Error getting interface',
-    '/org/freedesktop/portal/desktop',
+  /** @typedef {{ReadOne: (interface: string, settingName: string) => Promise<[unknown, ['prefer-dark' | 'prefer-light']]>}} ColorScheme */
+  /** @type {IfaceReturn<ColorScheme>} */
+  let iface = await bus.getObject(
+    'org.freedesktop.portal.Desktop',
     'org.freedesktop.portal.Settings',
+    '/org/freedesktop/portal/desktop',
   );
 
-  const [_, [value]] = await promisifyMethodAnnotate(
-    iface,
-    iface.ReadOne,
-    'Error reading color scheme',
-    'org.gnome.desktop.interface',
-    'color-scheme',
-  );
+  const [_, [value]] = await iface.ReadOne('org.gnome.desktop.interface', 'color-scheme');
   assert(value === 'prefer-dark' || value === 'prefer-light');
   return value;
 }
@@ -260,14 +362,11 @@ function writeAlacrittyColorConfigIfDifferent(cs) {
 
 /** Listen on the freedesktop SettingChanged dbus interface for the color-scheme setting to change. */
 async function listenForColorschemeChange() {
-  const service = bus.getService('org.freedesktop.portal.Desktop');
-
-  const iface = await promisifyMethodAnnotate(
-    service,
-    service.getInterface,
-    'Error getting interface',
-    '/org/freedesktop/portal/desktop',
+  /** @type {PromiseVal<ReturnType<typeof bus.getObject<{}>>>} */
+  const iface = await bus.getObject(
+    'org.freedesktop.portal.Desktop',
     'org.freedesktop.portal.Settings',
+    '/org/freedesktop/portal/desktop',
   );
 
   // Listen for SettingChanged signals
@@ -299,18 +398,12 @@ async function exportColorSchemeDbusInterface() {
   };
 
   try {
-    const retCode = await promisifyMethodAnnotate(
-      bus,
-      bus.requestName,
-      'Error requesting name for interface de.profpatsch.alacritty.ColorScheme',
-      ifaceName,
-      0,
-    );
+    bus;
+    const retCode = await bus.requestName(ifaceName, 0);
     console.log(
       `Request name returned ${retCode} for interface de.profpatsch.alacritty.ColorScheme`,
     );
     bus.exportInterface(ifaceImpl, '/de/profpatsch/alacritty/ColorScheme', iface);
-    bus.exportInterface(ifaceImpl, '/de/profpatsch/alacritty/ColorScheme2', iface);
     console.log('Exported interface de.profpatsch.alacritty.ColorScheme');
   } catch (err) {
     console.log('Error exporting interface de.profpatsch.alacritty.ColorScheme');
@@ -330,20 +423,12 @@ function annotateErr(msg) {
   };
 }
 
-/** @type any */
-const bus2 = dbus.sessionBus();
+const bus2 = new Bus('otel', 'session');
 async function exportOtelInterface() {
   console.log('Exporting OpenTelemetry interface');
 
   try {
-    const retCode = await promisifyMethodAnnotate(
-      bus2,
-      bus2.requestName,
-      'Error requesting name for interface de.profpatsch.otel.Tracer',
-
-      'de.profpatsch.otel.Tracer',
-      0,
-    );
+    const retCode = bus2.requestName('de.profpatsch.otel.Tracer', 0);
     console.log(
       `Request name returned ${retCode} for interface de.profpatsch.otel.Tracer`,
     );
@@ -442,49 +527,38 @@ async function getParentCallsite() {
 async function setupTracer(tracerName) {
   const parentCallsite = await getParentCallsite();
   console.log(`Setting up tracer ${tracerName} from ${parentCallsite?.getFileName()}`);
-  const service = bus2.getService('de.profpatsch.otel.Tracer');
-  const iface = await promisifyMethodAnnotate(
-    service,
-    service.getInterface,
-    'Error getting interface',
-    '/de/profpatsch/otel/TracerFactory',
+
+  /** @typedef {{CreateTracer: (name: string) => Promise<string>}} TracerFactory */
+  /** @type {IfaceReturn<TracerFactory>} */
+  const iface = await bus2.getObject(
+    'de.profpatsch.otel.Tracer',
     'de.profpatsch.otel.TracerFactory',
+    '/de/profpatsch/otel/TracerFactory',
   );
-  const path = await promisifyMethodAnnotate(
-    iface,
-    iface.CreateTracer,
-    'Error creating tracer',
-    tracerName,
-  );
-  const tracerIface = await promisifyMethodAnnotate(
-    service,
-    service.getInterface,
-    'Error getting interface',
-    path,
+  const path = await iface.CreateTracer(tracerName);
+
+  /**
+   *  @typedef {{
+   *    StartSpan: (spanData: string) => Promise<void>,
+   *    EndSpan: (spanData: string) => Promise<void>
+   *    BatchSpans: ([bool, string]) => Promise<void>
+   * }} Tracer
+   *  @type {IfaceReturn<Tracer>}
+   * */
+  const tracerIface = await bus2.getObject(
+    'de.profpatsch.otel.Tracer',
     'de.profpatsch.otel.Tracer',
+    path,
   );
 
   function StartSpan(spanData) {
-    return promisifyMethodAnnotate(
-      tracerIface,
-      tracerIface.StartSpan,
-      'Error starting span',
-      JSON.stringify(spanData),
-    );
+    return tracerIface.StartSpan(JSON.stringify(spanData));
   }
   function EndSpan(spanData) {
-    return promisifyMethodAnnotate(
-      tracerIface,
-      tracerIface.EndSpan,
-      'Error ending span',
-      JSON.stringify(spanData),
-    );
+    return tracerIface.EndSpan(JSON.stringify(spanData));
   }
   function BatchSpans(spans) {
-    return promisifyMethodAnnotate(
-      tracerIface,
-      tracerIface.BatchSpans,
-      'Error batching spans',
+    return tracerIface.BatchSpans(
       spans.map(([isStartSpan, span]) => [isStartSpan, JSON.stringify(span)]),
     );
   }
@@ -548,8 +622,6 @@ async function main() {
     [false, { spanId: 'batchy', endTime: t + 1000 }],
   ]);
 
-  // TODO: proper error handling, through proper callback promises for dbus function.
-
   await exportColorSchemeDbusInterface();
 
   // get the current color scheme