-
Notifications
You must be signed in to change notification settings - Fork 22
/
directory.ts
87 lines (78 loc) · 2.36 KB
/
directory.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
export type Path = string[];
export type Node<T> = File<T> | Folder<T>;
interface NodeBase<TType> {
type: TType;
}
export interface File<T> extends NodeBase<"file"> {
value: T;
}
export interface Folder<T> extends NodeBase<"folder"> {
value?: T;
children: { [key: string]: Node<T> };
}
export const empty: Folder<any> = Object.freeze({
type: "folder",
children: Object.freeze({}),
});
export function file<T>(value: T): File<T> {
return { type: "file", value };
}
export function textToPath(text: string, sep: string = "/"): Path {
return text.split(sep).filter(Boolean);
}
export function* walkFiles<T>(
node: Node<T>,
path: Path = [],
): Generator<[Path, File<T>]> {
if (node.type === "file") {
yield [path, node];
return;
}
for (const [name, child] of Object.entries(node.children)) {
if (child.type === "file") yield [[...path, name], child];
if (child.type === "folder") yield* walkFiles(child, [...path, name]);
}
}
export function* walkFolders<T>(
node: Node<T>,
path: Path = [],
): Generator<[Path, Folder<T>]> {
if (node.type === "file") return;
yield [path, node];
for (const [name, child] of Object.entries(node.children)) {
if (child.type === "folder") yield* walkFolders(child, [...path, name]);
}
}
export function has<T>(folder: Folder<T>, path: Path): boolean {
if (path.length < 1) return true;
const child = folder.children[path[0]];
if (path.length === 1) return !!child;
if (child.type === "file") return false;
return has(child, path.slice(1));
}
export function get<T>(node: Node<T>, path: Path): Node<T> | undefined {
if (path.length < 1) return node;
if (node.type === "file") return;
const child = node.children[path[0]];
if (!child) return;
if (path.length === 1) return child;
return get(child, path.slice(1));
}
export function set<T>(folder: Folder<T>, path: Path, value: T): Folder<T>;
export function set<T>(node: Node<T>, path: Path, value: T): Node<T>;
export function set<T>(node: Node<T>, path: Path, value: T): Node<T> {
if (path.length < 1) return { ...node, value };
const key = path[0];
const folder = node as Folder<T>;
const child = folder?.children?.[key];
return {
...node,
type: "folder",
children: {
...folder.children,
[key]: (!child && (path.length === 1))
? file(value)
: set(child ?? empty, path.slice(1), value),
},
};
}