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 | }
|
38 |
|
39 |
|
40 | return createNode(index + 1, node, routes)
|
41 | })(0, this.trie, routes)
|
42 | }
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | Trie.prototype.match = function (route) {
|
48 | assert.equal(typeof route, 'string', 'route should be a string')
|
49 |
|
50 | const routes = route.replace(/^\//, '').split('/')
|
51 | const params = {}
|
52 |
|
53 | var node = (function search (index, trie) {
|
54 |
|
55 | if (trie === undefined) return undefined
|
56 | const route = routes[index]
|
57 | if (route === undefined) return trie
|
58 |
|
59 | if (trie.nodes[route]) {
|
60 |
|
61 | return search(index + 1, trie.nodes[route])
|
62 | } else if (trie.name) {
|
63 |
|
64 | params[trie.name] = route
|
65 | return search(index + 1, trie.nodes[':'])
|
66 | } else {
|
67 |
|
68 | return search(index + 1)
|
69 | }
|
70 | })(0, this.trie)
|
71 |
|
72 | if (!node) return undefined
|
73 | node = xtend(node)
|
74 | node.params = params
|
75 | return node
|
76 | }
|
77 |
|
78 |
|
79 |
|
80 | Trie.prototype.mount = function (route, trie) {
|
81 | assert.equal(typeof route, 'string', 'route should be a string')
|
82 | assert.equal(typeof trie, 'object', 'trie should be a object')
|
83 |
|
84 | const split = route.replace(/^\//, '').split('/')
|
85 | var node = null
|
86 | var key = null
|
87 |
|
88 | if (split.length === 1) {
|
89 | key = split[0]
|
90 | node = this.create(key)
|
91 | } else {
|
92 | const headArr = split.splice(0, split.length - 1)
|
93 | const head = headArr.join('/')
|
94 | key = split[0]
|
95 | node = this.create(head)
|
96 | }
|
97 |
|
98 | mutate(node.nodes, trie.nodes)
|
99 | if (trie.name) node.name = trie.name
|
100 |
|
101 |
|
102 |
|
103 | if (node.nodes['']) {
|
104 | Object.keys(node.nodes['']).forEach(function (key) {
|
105 | if (key === 'nodes') return
|
106 | node[key] = node.nodes[''][key]
|
107 | })
|
108 | mutate(node.nodes, node.nodes[''].nodes)
|
109 | delete node.nodes[''].nodes
|
110 | }
|
111 | }
|