1 | 'use strict'
|
2 | let extend = require('util')._extend
|
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 | function Matcher (rules) {
|
28 | var options = rules.options
|
29 | delete rules.options
|
30 |
|
31 | this.rules = extend({}, Matcher.defaults.rules)
|
32 | this.rules = extend(this.rules, rules)
|
33 |
|
34 | this.options = extend({}, Matcher.defaults.options)
|
35 | this.options = extend(this.options, options)
|
36 |
|
37 | this._cache = {}
|
38 | }
|
39 |
|
40 | Matcher.defaults = {
|
41 | rules: {
|
42 | space: '\\s+'
|
43 | },
|
44 | options: {
|
45 | trim: false
|
46 | }
|
47 | }
|
48 |
|
49 | Matcher.prototype = {
|
50 | |
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | build (id) {
|
58 | var rule = this.partial(id)
|
59 | if (!rule) return
|
60 |
|
61 | var re = rule.regexp
|
62 | if (this.options.trim) re = '\\s*' + re + '\\s*'
|
63 |
|
64 | return {
|
65 | regexp: '^' + re + '$',
|
66 | indices: rule.indices
|
67 | }
|
68 | },
|
69 |
|
70 | partial (id) {
|
71 | if (this._cache[id]) return this._cache[id]
|
72 |
|
73 | var re
|
74 | var regexp = this.rules[id]
|
75 | if (!regexp) return
|
76 |
|
77 | if (regexp.many) {
|
78 | re = this.partialFromMany(regexp)
|
79 | } else {
|
80 | re = this.partialFromRegexp(regexp)
|
81 | }
|
82 |
|
83 | this._cache[id] = re
|
84 | return re
|
85 | },
|
86 |
|
87 | |
88 |
|
89 |
|
90 |
|
91 | partialFromRegexp (regexp) {
|
92 | var indices = []
|
93 | var matcher = this
|
94 |
|
95 |
|
96 | if (regexp.constructor === RegExp) regexp = regexp.source
|
97 |
|
98 |
|
99 | regexp = regexp.replace(/ /g, this.rules.space)
|
100 | regexp = escapeGroups(regexp)
|
101 |
|
102 |
|
103 | regexp = regexp.replace(/%\{(?:([A-Za-z0-9]+):)?([A-Za-z0-9]+)\}/g, function (_, alias, id) {
|
104 | var r = matcher.partial(id)
|
105 | indices.push(alias || id)
|
106 | indices = indices.concat(r.indices)
|
107 | return `(${r.regexp})`
|
108 | })
|
109 |
|
110 | return { regexp: regexp, indices: indices }
|
111 | },
|
112 |
|
113 | partialFromMany (def) {
|
114 | if (!def.separator) throw new Error('No separator')
|
115 |
|
116 | var many = def.many
|
117 | var sep = def.separator
|
118 | if (sep.source) sep = sep.source
|
119 |
|
120 | var regexp = this.partial(many).regexp
|
121 | regexp = escapeGroups(regexp)
|
122 | regexp = `${regexp}(?:${sep}${regexp})*`
|
123 |
|
124 | return { regexp: regexp, indices: [] }
|
125 | },
|
126 |
|
127 | |
128 |
|
129 |
|
130 |
|
131 | match (id, str) {
|
132 | var matcher = this.build(id)
|
133 | if (!matcher) return
|
134 |
|
135 | var m = str.match(matcher.regexp)
|
136 | if (!m) return false
|
137 |
|
138 | var output = {}
|
139 | output[id] = m[0]
|
140 |
|
141 | if (matcher.indices.length > 0) {
|
142 | matcher.indices.forEach(function (name, i) {
|
143 | output[name] = m[i + 1]
|
144 | })
|
145 | }
|
146 |
|
147 | return output
|
148 | },
|
149 |
|
150 | |
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 | multi (ids, str) {
|
157 | for (var i in ids) {
|
158 | var id = ids[i]
|
159 | var output = this.match(id, str)
|
160 | if (output) {
|
161 | if (typeof output === 'object') {
|
162 | output.rule = id
|
163 | return output
|
164 | } else {
|
165 | return {
|
166 | value: output,
|
167 | rule: id
|
168 | }
|
169 | }
|
170 | }
|
171 | }
|
172 |
|
173 | return false
|
174 | },
|
175 |
|
176 | |
177 |
|
178 |
|
179 |
|
180 | switch (str, callbacks) {
|
181 | var ids = Object.keys(callbacks)
|
182 | var m = this.multi(ids, str)
|
183 |
|
184 | if (!m) return callbacks.else ? callbacks.else(m) : false
|
185 |
|
186 | return callbacks[m.rule](m)
|
187 | }
|
188 | }
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 | function escapeGroups (regexp) {
|
195 | return regexp.replace(/^\(|[^\\]\((?!\?:)/g, function (match) {
|
196 | return match + '?:'
|
197 | })
|
198 | }
|
199 |
|
200 | module.exports = Matcher
|