-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathjson-storage.ts
80 lines (72 loc) · 2.63 KB
/
json-storage.ts
1
2
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import BigNumber from 'bignumber.js'
import { UnexpectedMarkException, UnexpectedParamsException, UnexpectedTypeException } from '../exceptions'
import { ChainStorage } from './chain-storage'
export type JSONStorageType = string | boolean | BigNumber
export type JSONStorageOffChain =
| JSONStorageType
| JSONStorageOffChain[]
| {
[x: string]: JSONStorageOffChain
}
const TYPE_MARK_LEN = 1
const BIG_NUMBER_TYPE = 'bignumber'
const TYPE_MARK_MAP = {
string: '0',
boolean: '1',
[BIG_NUMBER_TYPE]: '2',
}
function reviver(key: string, value: JSONStorageOffChain | JSONStorageType) {
if (value === null) throw new UnexpectedTypeException('null')
if (typeof value === 'object') {
return value
}
const mark = value.toString().slice(0, TYPE_MARK_LEN)
if (mark.length !== TYPE_MARK_LEN) {
throw new UnexpectedMarkException(mark)
}
const acturalValue = value.toString().slice(TYPE_MARK_LEN)
switch (mark) {
case TYPE_MARK_MAP['string']:
return acturalValue
case TYPE_MARK_MAP['boolean']:
return acturalValue === 'true'
case TYPE_MARK_MAP['bignumber']:
return BigNumber(acturalValue)
default:
throw new UnexpectedMarkException(mark)
}
}
function serializeSimpleType(v: string | boolean) {
const valueType = typeof v
if (v === null) throw new UnexpectedTypeException('null')
const mark = TYPE_MARK_MAP[valueType as keyof typeof TYPE_MARK_MAP]
if (mark) {
return `${mark}${v.toString()}`
}
throw new UnexpectedTypeException(valueType)
}
type AddMarkStorage = string | AddMarkStorage[] | { [key: string]: AddMarkStorage }
export function addMarkForStorage(data?: JSONStorageOffChain): AddMarkStorage {
if (data === null || data === undefined) throw new UnexpectedTypeException(`${data}`)
if (data instanceof BigNumber) return `${TYPE_MARK_MAP[BIG_NUMBER_TYPE]}${data.toFixed()}`
if (typeof data !== 'object') return serializeSimpleType(data)
if (Array.isArray(data)) {
return data.map((v) => addMarkForStorage(v))
}
const newData: Record<string, AddMarkStorage> = {}
Object.keys(data).forEach((key: string) => {
newData[key] = addMarkForStorage(data[key])
})
return newData
}
export class JSONStorage<T extends JSONStorageOffChain> extends ChainStorage<T> {
serialize(data: T): Uint8Array {
return Buffer.from(JSON.stringify(addMarkForStorage(data)))
}
deserialize(data: Uint8Array): T {
if (data === null || data === undefined) throw new UnexpectedParamsException(`${data}`)
const json = Buffer.from(data).toString()
if (json === '') throw new UnexpectedParamsException(`${data}`)
return JSON.parse(Buffer.from(data).toString(), reviver)
}
}