refactor(users/Profpatsch): simple wrapper around the dbus lib
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
This commit is contained in:
parent
4eeac3cb1d
commit
c12fdeb8e0
1 changed files with 161 additions and 89 deletions
|
@ -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',
|
||||
'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,
|
||||
|
||||
/** @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 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
|
||||
|
|
Loading…
Reference in a new issue