1 | const mutate = require('xtend/mutable')
|
2 | const assert = require('assert')
|
3 | const xtend = require('xtend')
|
4 |
|
5 | module.exports = Trie
|
6 |
|
7 |
|
8 |
|
9 | function Trie () {
|
10 | if (!(this instanceof Trie)) return new Trie()
|
11 | this.trie = { nodes: {} }
|
12 | }
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | Trie.prototype.create = function (route) {
|
18 | assert.equal(typeof route, 'string', 'route should be a string')
|
19 |
|
20 | const routes = route.replace(/^\//, '').split('/')
|
21 | return (function createNode (index, trie, routes) {
|
22 | const route = routes[index]
|
23 |
|
24 | if (route === undefined) return trie
|
25 |
|
26 | var node = null
|
27 | if (/^:/.test(route)) {
|
28 |
|
29 | if (!trie.nodes[':']) {
|
30 | node = { nodes: {} }
|
31 | trie.nodes[':'] = node
|
32 | }
|
33 | trie.name = route.replace(/^:/, '')
|
34 | } else if (!trie.nodes[route]) {
|
35 | node = { nodes: {} }
|
36 | trie.nodes[route] = node
|
37 | } else {
|
38 | node = trie.nodes[route]
|
39 | }
|
40 |
|
41 |
|
42 | return createNode(index + 1, node, routes)
|
43 | })(0, this.trie, routes)
|
44 | }
|
45 |
|
46 |
|
47 |
|
48 |
|
49 | Trie.prototype.match = function (route) {
|
50 | assert.equal(typeof route, 'string', 'route should be a string')
|
51 |
|
52 | const routes = route.replace(/^\//, '').split('/')
|
53 | const params = {}
|
54 |
|
55 | var node = (function search (index, trie) {
|
56 |
|
57 | if (trie === undefined) return undefined
|
58 | const route = routes[index]
|
59 | if (route === undefined) return trie
|
60 |
|
61 | if (trie.nodes[route]) {
|
62 |
|
63 | return search(index + 1, trie.nodes[route])
|
64 | } else if (trie.name) {
|
65 |
|
66 | params[trie.name] = route
|
67 | return search(index + 1, trie.nodes[':'])
|
68 | } else {
|
69 |
|
70 | return search(index + 1)
|
71 | }
|
72 | })(0, this.trie)
|
73 |
|
74 | if (!node) return undefined
|
75 | node = xtend(node)
|
76 | node.params = params
|
77 | return node
|
78 | }
|
79 |
|
80 |
|
81 |
|
82 | Trie.prototype.mount = function (route, trie) {
|
83 | assert.equal(typeof route, 'string', 'route should be a string')
|
84 | assert.equal(typeof trie, 'object', 'trie should be a object')
|
85 |
|
86 | const split = route.replace(/^\//, '').split('/')
|
87 | var node = null
|
88 | var key = null
|
89 |
|
90 | if (split.length === 1) {
|
91 | key = split[0]
|
92 | node = this.create(key)
|
93 | } else {
|
94 | const headArr = split.splice(0, split.length - 1)
|
95 | const head = headArr.join('/')
|
96 | key = split[0]
|
97 | node = this.create(head)
|
98 | }
|
99 |
|
100 | mutate(node.nodes, trie.nodes)
|
101 | if (trie.name) node.name = trie.name
|
102 |
|
103 |
|
104 |
|
105 | if (node.nodes['']) {
|
106 | Object.keys(node.nodes['']).forEach(function (key) {
|
107 | if (key === 'nodes') return
|
108 | node[key] = node.nodes[''][key]
|
109 | })
|
110 | mutate(node.nodes, node.nodes[''].nodes)
|
111 | delete node.nodes[''].nodes
|
112 | }
|
113 | }
|