rpcchannel
Version:
Easy RPC with permission controls
112 lines (98 loc) • 2.73 kB
text/typescript
/**
* A mapping of multi-part addresses (using the Java naming convention) to any
* type. Used extensively by the RPC channel.
* @author Nathan Pennie <kb1rd@kb1rd.net>
*/
/** */
import { isUndef } from './utils'
type MultistringAddress = string[]
type WildcardMultistringAddress = (string | undefined | null)[]
const DefaultEntryKey = Symbol('DefaultEntry')
const WildcardEntryKey = Symbol('WildcardEntry')
type AddressMapFlat<T> = {
[key: string]: AddressMapFlat<T>
[WildcardEntryKey]?: AddressMapFlat<T>
[DefaultEntryKey]?: T
}
function getFromAddrMapFlat<T>(
map: AddressMapFlat<T>,
addr: MultistringAddress,
wc_values?: string[]
): T | undefined {
const part = addr.shift()
if (!part) {
return map[DefaultEntryKey]
}
let data: T | undefined = undefined
if (map[part]) {
data = getFromAddrMapFlat(map[part], addr, wc_values)
}
if (!data && map[WildcardEntryKey]) {
if (wc_values) {
wc_values.push(part)
}
data = getFromAddrMapFlat(
map[WildcardEntryKey] as AddressMapFlat<T>,
addr,
wc_values
)
}
addr.unshift(part)
return data
}
class AddressMap<T> {
table: AddressMapFlat<T> = {}
put(addr: WildcardMultistringAddress, value: T | undefined): void {
let last_table = this.table
addr.forEach((part) => {
const key = part || WildcardEntryKey
if (isUndef(last_table[key])) {
last_table[key] = {}
}
last_table = last_table[key] as AddressMapFlat<T>
})
// TODO: Delete parents if empty
if (isUndef(value)) {
delete last_table[DefaultEntryKey]
} else {
last_table[DefaultEntryKey] = value
}
}
get(addr: MultistringAddress, wc_values?: string[]): T | undefined {
const value = getFromAddrMapFlat(this.table, addr, wc_values)
return isUndef(value) ? this.table[DefaultEntryKey] : value
}
clear(): void {
for (const k of Object.keys(this.table)) {
delete this.table[k]
}
}
toString(): string {
const entries: string[] = []
const addr_str: string[] = []
function traverse(map: AddressMapFlat<T>) {
if (map[DefaultEntryKey]) {
entries.push(`${addr_str.join('.')}: ${map[DefaultEntryKey]}`)
}
if (map[WildcardEntryKey]) {
addr_str.push('*')
traverse(map[WildcardEntryKey] as AddressMapFlat<T>)
addr_str.pop()
}
Object.keys(map).forEach((key) => {
addr_str.push(`[${key}]`)
traverse(map[key])
addr_str.pop()
})
}
traverse(this.table)
return `AddrMap [\n${entries.map((s) => ' ' + s).join('\n')}\n]`
}
}
export {
MultistringAddress,
WildcardMultistringAddress,
AddressMap,
DefaultEntryKey,
WildcardEntryKey
}