Skip to content

Commit d2385a3

Browse files
committed
refactor: with results
1 parent c130366 commit d2385a3

9 files changed

Lines changed: 174 additions & 175 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"react-dom": "^18.2.0",
7171
"react-redux": "^9.1.0",
7272
"react-use": "^17.5.0",
73+
"ts-results": "^3.3.0",
7374
"typescript": "^5.3.3",
7475
"universal-analytics": "^0.5.3"
7576
},

src/main/utils.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,8 @@
1-
import fs from "fs";
21
import { machineId } from "node-machine-id";
32
import os from "os";
43
// import { setUpdateNotification } from 'electron-update-notification' // TODO:
54
import ua from "universal-analytics";
65

7-
export async function readdirSafe(p: string) {
8-
try {
9-
return await fs.promises.readdir(p);
10-
} catch (err) {
11-
console.error(err);
12-
return [];
13-
}
14-
}
15-
16-
export async function readFileSafe(p: string) {
17-
try {
18-
return await fs.promises.readFile(p, { encoding: "utf8" });
19-
} catch (err) {
20-
console.error(err);
21-
return "";
22-
}
23-
}
24-
25-
export async function readFileAsBufferSafe(p: string) {
26-
try {
27-
return await fs.promises.readFile(p);
28-
} catch (err) {
29-
console.error(err);
30-
return;
31-
}
32-
}
33-
346
export async function setReporter() {
357
try {
368
const id = await machineId();

src/platforms/linux.ts

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,80 @@
1-
import { readdirSafe, readFileSafe } from "../main/utils";
2-
import type { AppInfo } from "../reducers/app";
31
import type { AppReader } from "./utils";
42
import fs from "fs";
53
import ini from "ini";
64
import path from "path";
5+
import { Result } from "ts-results";
76

87
const desktopFilesDir = "/usr/share/applications";
98

10-
async function readAppInfo(desktopFile: string): Promise<AppInfo | undefined> {
11-
const content = await readFileSafe(desktopFile);
9+
const readAppInfo = (desktopFile: string) =>
10+
Result.wrapAsync(async () => {
11+
const content = await fs.promises.readFile(desktopFile, {
12+
encoding: "utf-8",
13+
});
14+
const entry = ini.parse(content)["Desktop Entry"] as
15+
| {
16+
Name?: string;
17+
Icon?: string;
18+
Exec?: string;
19+
}
20+
| undefined;
1221

13-
const entry = ini.parse(content)["Desktop Entry"] as {
14-
Name?: string;
15-
Icon?: string;
16-
Exec?: string;
17-
};
18-
if (!entry || !entry.Exec) return;
22+
if (!entry?.Exec) throw new Error("Exec not found");
1923

20-
let exePath = "";
21-
if (entry.Exec.startsWith('"')) {
22-
exePath = entry.Exec.replace(/^"(.*)".*/, "$1");
23-
} else {
24-
// Remove arg
25-
exePath = entry.Exec.split(/\s+/)[0] ?? "";
26-
}
27-
28-
if (!exePath.startsWith("/")) return;
29-
30-
if (!fs.existsSync(path.join(exePath, "../resources/electron.asar"))) return;
24+
let exePath = "";
25+
if (entry.Exec.startsWith('"')) {
26+
exePath = entry.Exec.replace(/^"(.*)".*/, "$1");
27+
} else {
28+
// Remove arg
29+
exePath = entry.Exec.split(/\s+/)[0] ?? "";
30+
}
3131

32-
let icon = "";
33-
if (entry.Icon) {
34-
try {
35-
const iconBuffer = await fs.promises.readFile(
36-
`/usr/share/icons/hicolor/1024x1024/apps/${entry.Icon}.png`,
37-
);
38-
icon = "data:image/png;base64," + iconBuffer.toString("base64");
39-
} catch (err) {
40-
console.error(err);
32+
if (!exePath.startsWith("/")) {
33+
throw new Error("Exec path invalid");
34+
}
35+
if (!fs.existsSync(path.join(exePath, "../resources/electron.asar"))) {
36+
throw new Error("resources/electron.asar not exists");
4137
}
42-
}
4338

44-
return {
45-
id: exePath,
46-
icon: icon, // TODO: Read icon
47-
name: entry.Name || path.basename(exePath),
48-
exePath: exePath,
49-
};
50-
}
39+
let icon = "";
40+
if (entry.Icon) {
41+
try {
42+
const iconBuffer = await fs.promises.readFile(
43+
`/usr/share/icons/hicolor/1024x1024/apps/${entry.Icon}.png`,
44+
);
45+
icon = "data:image/png;base64," + iconBuffer.toString("base64");
46+
} catch (err) {
47+
console.error(err);
48+
}
49+
}
5150

52-
export const adapter: AppReader = {
53-
async readAll() {
54-
const files = await readdirSafe(desktopFilesDir);
55-
const apps = await Promise.all(
56-
files.map((file) => {
57-
if (!file.endsWith(".desktop")) return;
58-
return readAppInfo(path.join(desktopFilesDir, file));
59-
}),
60-
);
61-
return apps;
62-
},
63-
async readByPath(p: string) {
6451
return {
65-
id: p,
66-
name: path.basename(p),
67-
icon: "",
68-
exePath: p,
52+
id: exePath,
53+
icon: icon, // TODO: Read icon
54+
name: entry.Name || path.basename(exePath),
55+
exePath: exePath,
6956
};
70-
},
57+
});
58+
59+
export const adapter: AppReader = {
60+
readAll: () =>
61+
Result.wrapAsync(async () => {
62+
const files = await fs.promises.readdir(desktopFilesDir);
63+
const apps = await Promise.all(
64+
files
65+
.filter((f) => f.endsWith(".desktop"))
66+
.map((file) => readAppInfo(path.join(desktopFilesDir, file))),
67+
);
68+
return apps.flatMap((app) => (app.ok ? [app.unwrap()] : []));
69+
}),
70+
readByPath: (p: string) =>
71+
Result.wrapAsync(async () => {
72+
// TODO:
73+
return {
74+
id: p,
75+
name: path.basename(p),
76+
icon: "",
77+
exePath: p,
78+
};
79+
}),
7180
};

src/platforms/macos.ts

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { readdirSafe, readFileAsBufferSafe } from "../main/utils";
21
import type { AppReader } from "./utils";
32
import fs from "fs";
43
import path from "path";
54
import plist from "simple-plist";
5+
import { Result } from "ts-results";
66

77
interface MacosAppInfo {
88
CFBundleIdentifier: string;
@@ -26,7 +26,7 @@ async function readPlistFile(path: string) {
2626
}
2727

2828
async function readIcnsAsImageUri(file: string) {
29-
let buf = await readFileAsBufferSafe(file);
29+
let buf = await fs.promises.readFile(file);
3030
if (!buf) return "";
3131

3232
const totalSize = buf.readInt32BE(4) - 8;
@@ -55,28 +55,32 @@ async function readIcnsAsImageUri(file: string) {
5555
}
5656

5757
export const adapter: AppReader = {
58-
async readAll() {
59-
const dir = "/Applications";
60-
const appPaths = await readdirSafe(dir);
61-
return Promise.all(appPaths.map((p) => this.readByPath(path.join(dir, p))));
62-
},
63-
async readByPath(p: string) {
64-
const isElectronBased = fs.existsSync(
65-
path.join(p, "Contents/Frameworks/Electron Framework.framework"),
66-
);
67-
if (!isElectronBased) return;
58+
readAll: () =>
59+
Result.wrapAsync(async () => {
60+
const dir = "/Applications";
61+
const appPaths = await fs.promises.readdir(dir);
62+
const apps = await Promise.all(
63+
appPaths.map((p) => adapter.readByPath(path.join(dir, p))),
64+
);
65+
return apps.flatMap((app) => (app.ok ? [app.unwrap()] : []));
66+
}),
67+
readByPath: (p: string) =>
68+
Result.wrapAsync(async () => {
69+
const isElectronBased = fs.existsSync(
70+
path.join(p, "Contents/Frameworks/Electron Framework.framework"),
71+
);
72+
if (!isElectronBased) throw new Error("Not an electron app");
6873

69-
const info = await readPlistFile(path.join(p, "Contents/Info.plist"));
74+
const info = await readPlistFile(path.join(p, "Contents/Info.plist"));
75+
const icon = await readIcnsAsImageUri(
76+
path.join(p, "Contents/Resources", info.CFBundleIconFile),
77+
);
7078

71-
const icon = await readIcnsAsImageUri(
72-
path.join(p, "Contents/Resources", info.CFBundleIconFile),
73-
);
74-
75-
return {
76-
id: info.CFBundleIdentifier,
77-
name: info.CFBundleName,
78-
icon,
79-
exePath: path.resolve(p, "Contents/MacOS", info.CFBundleExecutable),
80-
};
81-
},
79+
return {
80+
id: info.CFBundleIdentifier,
81+
name: info.CFBundleName,
82+
icon,
83+
exePath: path.resolve(p, "Contents/MacOS", info.CFBundleExecutable),
84+
};
85+
}),
8286
};

src/platforms/utils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { AppInfo } from "../reducers/app";
2+
import { Result } from "ts-results";
23

34
export interface AppReader {
4-
readAll(): Promise<(AppInfo | undefined)[]>;
5-
readByPath(p: string): Promise<AppInfo | undefined>;
5+
readAll(): Promise<Result<AppInfo[], Error>>;
6+
readByPath(p: string): Promise<Result<AppInfo, Error>>;
67
}

0 commit comments

Comments
 (0)