-
Notifications
You must be signed in to change notification settings - Fork 1
/
path-tree.ts
114 lines (104 loc) · 3.06 KB
/
path-tree.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
export interface IPathNode<T> {
key?: string;
value?: T;
children?: {[key: string]: IPathNode<T>};
leaf?: boolean;
parentNode?: IPathNode<T>;
}
/**
* PathTree - represents a path as a tree structure
*/
export class PathTree<T> {
private rootNode: IPathNode<T> = {
children: {},
};
/**
* Adds a path
* @param {string} path
* @param {object} value
*/
add(path: string, value: T) {
const noSlashPath = this.removeExtraSlashes(path);
const pathArray = noSlashPath.split("/");
const pathArrLength = pathArray.length;
let currentNode: IPathNode<T> = this.rootNode;
let parentNode: IPathNode<T> = null;
for (let i = 0; i < pathArrLength; i++) {
// check params
const key = pathArray[i][0] === ":" ? ":" : pathArray[i];
if (key in currentNode.children) {
if (i === pathArrLength - 1 && currentNode.children[key].leaf) {
console.warn(`path-router: Warning duplicate key ${noSlashPath} in the path tree`);
}
} else {
const newNode = this.createNode(pathArray[i], pathArrLength - 1, i, value, parentNode);
currentNode.children[key] = newNode;
}
parentNode = currentNode;
currentNode = currentNode.children[key];
}
}
/**
* Gets a value by path
* @param {string} path
* @return {object}
*/
get(path: string): T {
const normalizedPath = this.removeExtraSlashes(path);
const pathArray = normalizedPath.split("/");
const pathArrLength = pathArray.length;
let value: T;
let currentNode = this.rootNode;
let currentTree = currentNode.children;
for (let i = 0; i < pathArrLength; i++) {
if (pathArray[i] in currentTree) {
if ((i === pathArrLength - 1) && currentTree[pathArray[i]].leaf) {
value = currentTree[pathArray[i]].value;
return value;
}
currentTree = currentTree[pathArray[i]].children;
} else if (":" in currentTree) {
if ((i === pathArrLength - 1) && currentTree[":"].leaf) {
value = currentTree[":"].value;
return value;
}
currentNode = currentTree[":"];
if (!currentNode) {
break;
}
currentTree = currentNode.children;
}
}
for (let i = pathArrLength - 1; i >= 0; i--) {
if ("*" in currentTree) {
return currentTree["*"].value;
} else {
currentNode = currentNode.parentNode;
if (!currentNode) {
break;
}
currentTree = currentNode && currentNode.children;
}
}
}
/**
* Clears all existings pathes
*/
clear() {
this.rootNode = {
children: {},
};
}
private createNode(key: string, lastIndex: number, index: number, value: T, parentNode: IPathNode<T>): IPathNode<T> {
return {
key: key[0] === ":" ? key.substring(1, key.length) : key,
value: index === lastIndex ? value : null,
children: {},
leaf: index === lastIndex,
parentNode: parentNode,
};
}
private removeExtraSlashes(path: string) {
return path.replace(/\/$/, "").replace(/^\//, "");
}
}