1 | import { findIndex } from 'lodash'
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | export default class PrefixMap<T> {
|
17 | protected prefixes: string[]
|
18 | protected items: { [key: string]: T }
|
19 |
|
20 | constructor () {
|
21 | this.prefixes = []
|
22 | this.items = {}
|
23 | }
|
24 |
|
25 | keys () { return this.prefixes }
|
26 |
|
27 | size () { return this.prefixes.length }
|
28 |
|
29 | |
30 |
|
31 |
|
32 | resolve (key: string) {
|
33 | const prefix = this.resolvePrefix(key)
|
34 |
|
35 | return typeof prefix !== 'undefined' ? this.items[prefix] : undefined
|
36 | }
|
37 |
|
38 | |
39 |
|
40 |
|
41 | resolvePrefix (key: string) {
|
42 |
|
43 | if (this.items[key]) return key
|
44 |
|
45 | const index = findIndex(this.prefixes, (e: string) => key.startsWith(e + '.'))
|
46 | if (index === -1) return undefined
|
47 | const prefix = this.prefixes[index]
|
48 | return prefix
|
49 | }
|
50 |
|
51 | get (prefix: string): T | undefined { return this.items[prefix] }
|
52 |
|
53 | |
54 |
|
55 |
|
56 | * getKeysStartingWith (prefix: string): IterableIterator<string> {
|
57 |
|
58 | const predicate = (key: string) => key.startsWith(prefix)
|
59 | let index = -1
|
60 |
|
61 | while ((index = findIndex(this.prefixes, predicate, index + 1)) !== -1) {
|
62 | yield this.prefixes[index]
|
63 | }
|
64 | }
|
65 |
|
66 | * getKeysPrefixesOf (search: string): IterableIterator<string> {
|
67 | const predicate = (key: string) => search.startsWith(key + '.')
|
68 | let index = -1
|
69 |
|
70 | while ((index = findIndex(this.prefixes, predicate, index + 1)) !== -1) {
|
71 | yield this.prefixes[index]
|
72 | }
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 | each (fn: (item: T, key: string) => void) {
|
79 | for (const prefix of this.prefixes) {
|
80 | fn(this.items[prefix], prefix)
|
81 | }
|
82 | }
|
83 |
|
84 | |
85 |
|
86 |
|
87 |
|
88 | insert (prefix: string, item: T) {
|
89 | if (!this.items[prefix]) {
|
90 | const index = findIndex(this.prefixes, (e) => {
|
91 | if (prefix.length === e.length) {
|
92 | return prefix > e
|
93 | }
|
94 | return prefix.length > e.length
|
95 | })
|
96 |
|
97 | if (index === -1) {
|
98 | this.prefixes.push(prefix)
|
99 | } else {
|
100 | this.prefixes.splice(index, 0, prefix)
|
101 | }
|
102 | }
|
103 | this.items[prefix] = item
|
104 | return item
|
105 | }
|
106 |
|
107 | delete (prefix: string) {
|
108 | const index = this.prefixes.indexOf(prefix)
|
109 | if (this.prefixes[index] === prefix) this.prefixes.splice(index, 1)
|
110 | delete this.items[prefix]
|
111 | }
|
112 |
|
113 | toJSON () {
|
114 | return this.items
|
115 | }
|
116 |
|
117 | |
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | getShortestUnambiguousPrefix (address: string, prefix = '') {
|
133 | if (!address.startsWith(prefix)) {
|
134 | throw new Error(`address must start with prefix. address=${address} prefix=${prefix}`)
|
135 | }
|
136 |
|
137 | this.keys().forEach((secondPrefix: string) => {
|
138 | if (secondPrefix === prefix) {
|
139 | return
|
140 | }
|
141 |
|
142 | while (secondPrefix.startsWith(prefix)) {
|
143 | if (secondPrefix === prefix) {
|
144 | return
|
145 | }
|
146 |
|
147 | const nextSegmentEnd = address.indexOf('.', prefix.length + 1)
|
148 |
|
149 | if (nextSegmentEnd === -1) {
|
150 | prefix = address
|
151 | return
|
152 | } else {
|
153 | prefix = address.slice(0, nextSegmentEnd)
|
154 | }
|
155 | }
|
156 | })
|
157 |
|
158 | return prefix
|
159 | }
|
160 | }
|