166 lines
4.4 KiB
TypeScript
166 lines
4.4 KiB
TypeScript
import Gio from "gi://Gio";
|
|
import GLib from "gi://GLib";
|
|
import { readFileAsync } from "astal/file";
|
|
import GObject from "astal/gobject";
|
|
|
|
export async function readFromStreamRaw(
|
|
stream: Gio.InputStream,
|
|
bytes: number,
|
|
): Promise<Uint8Array> {
|
|
return new Promise<Uint8Array>((resolve, reject) => {
|
|
stream.read_bytes_async(
|
|
bytes,
|
|
GLib.PRIORITY_DEFAULT,
|
|
null,
|
|
(stream, result) => {
|
|
try {
|
|
const data = stream!.read_bytes_finish(result);
|
|
const buffer = data.get_data();
|
|
if (buffer === null) {
|
|
reject(new Error("Failed to read from stream"));
|
|
return;
|
|
}
|
|
resolve(buffer);
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
export async function readFromStream(
|
|
stream: Gio.InputStream,
|
|
bytes: number,
|
|
): Promise<Uint8Array> {
|
|
const chunkCount = Math.ceil(bytes / 4096);
|
|
const buffer = await Array.from({ length: chunkCount }, (_, i) => i).reduce(
|
|
async (acc, i) => {
|
|
const buffer = await acc;
|
|
const chunkSize = Math.min(4096, bytes - i * 4096);
|
|
const chunk = await readFromStreamRaw(stream, chunkSize);
|
|
buffer.set(chunk, i * 4096);
|
|
return buffer;
|
|
},
|
|
Promise.resolve(new Uint8Array(bytes)),
|
|
);
|
|
return buffer;
|
|
}
|
|
|
|
export async function writeToStream(
|
|
stream: Gio.OutputStream,
|
|
data: ArrayBuffer | Uint8Array,
|
|
): Promise<void> {
|
|
if (data instanceof ArrayBuffer) {
|
|
data = new Uint8Array(data);
|
|
}
|
|
return new Promise<void>((resolve, reject) => {
|
|
stream.write_all_async(
|
|
data as Uint8Array,
|
|
GLib.PRIORITY_DEFAULT,
|
|
null,
|
|
(stream, result) => {
|
|
try {
|
|
stream?.write_all_finish(result) ??
|
|
reject(new Error("Failed to write to stream"));
|
|
resolve();
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
export async function dereferenceSymbolicLink(
|
|
filename: string,
|
|
): Promise<string> {
|
|
return new Promise<string>((resolve, reject) => {
|
|
console.log(`Dereferencing symbolic link for ${filename}`);
|
|
const file = Gio.File.new_for_path(filename);
|
|
file.query_info_async(
|
|
"standard::is-symlink,standard::symlink-target",
|
|
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
|
|
GLib.PRIORITY_DEFAULT,
|
|
null,
|
|
(file, result) => {
|
|
try {
|
|
const info = file!.query_info_finish(result);
|
|
if (info.get_is_symlink()) {
|
|
console.log("File is a symlink");
|
|
const target = info.get_symlink_target() as string;
|
|
console.log(target);
|
|
if (target.startsWith("./") || target.startsWith("../")) {
|
|
resolve(file!.resolve_relative_path(target).get_path()!);
|
|
} else if (target.startsWith("/")) {
|
|
resolve(target);
|
|
} else {
|
|
resolve(file!.get_parent()!.get_child(target).get_path()!);
|
|
}
|
|
resolve(file!.resolve_relative_path(target).get_path()!);
|
|
} else {
|
|
resolve(filename);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
reject(e);
|
|
}
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
const dbusXml = new Map<string, string>();
|
|
|
|
export async function getDbusXml(ifaceName: string) {
|
|
if (dbusXml.has(ifaceName)) {
|
|
return dbusXml.get(ifaceName) as string;
|
|
}
|
|
const contents = await readFileAsync(`dbus/${ifaceName}.xml`);
|
|
dbusXml.set(ifaceName, contents);
|
|
return contents;
|
|
}
|
|
|
|
declare global {
|
|
// Add new "rotate" method to Array
|
|
interface Array<T> {
|
|
/**
|
|
* Rotates the array by `n` positions.
|
|
*
|
|
* @param n The number of positions to rotate the array by.
|
|
* @returns The rotated array.
|
|
*/
|
|
rotate(n: number): T[];
|
|
|
|
/**
|
|
* Asynchronous version of the map() function.
|
|
*/
|
|
amap<U>(
|
|
callbackfn: (value: T, index: number, array: T[]) => Promise<U>,
|
|
): Promise<U[]>;
|
|
}
|
|
}
|
|
|
|
Array.prototype.amap = function <T>(
|
|
this: unknown[],
|
|
callbackfn: (value: unknown, index: number, array: unknown[]) => Promise<T>,
|
|
): Promise<T[]> {
|
|
return Promise.all(this.map(callbackfn));
|
|
};
|
|
|
|
Array.prototype.rotate = function <T>(this: T[], n: number): T[] {
|
|
const array = this;
|
|
const length = array.length;
|
|
if (length === 0) {
|
|
return [];
|
|
}
|
|
n = ((n % length) + length) % length;
|
|
return array.slice(n).concat(array.slice(0, n));
|
|
};
|
|
|
|
export async function delay(ms: number) {
|
|
return new Promise<void>((resolve) => {
|
|
setTimeout(resolve, ms);
|
|
});
|
|
}
|