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