-
Notifications
You must be signed in to change notification settings - Fork 4
/
model.ts
108 lines (100 loc) · 2.74 KB
/
model.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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
type Record<T> = { value: T }
export class DataModel<T extends object> {
private records: Set<Record<T>>
private indexes: Map<T, Record<T>>
constructor(values: T[] = []) {
this.records = new Set()
this.indexes = new Map()
values.forEach(ref => {
const record = this.toRecord(ref)
this.records.add(record)
this.indexes.set(ref, record)
})
}
private toRecord(item: T): Record<T> {
return { value: item }
}
private fromRecord(item: Record<T>): T {
return item.value
}
toJSON(): T[] {
const acc: T[] = []
this.records.forEach(record => {
acc.push(this.fromRecord(record))
})
return acc
}
fork(): DataModel<T> {
return new DataModel(this.toJSON())
}
/// IDEMPOTENT ///
count(): number {
return this.records.size
}
find(filter?: (item: T) => true): T[] {
const json = this.toJSON()
if(undefined === filter) return json
return json.filter(filter)
}
findOne(filter?: (item: T) => true): T | null {
if(undefined === filter) return null
return this.toJSON().find(filter) || null
}
/// MODIFIER ///
private _update(oldItem: T, newItem: T, upsert: boolean = false) {
const record = this.indexes.get(oldItem)
if(undefined === record) {
if(upsert) return this.create(newItem)
return null
}
record.value = newItem
return this
}
update(oldItem: T, newItem: T, upsert: boolean = false): DataModel<T> {
const ret = this._update(oldItem, newItem, upsert)
if(null === ret) return this
return this.fork()
}
updateMany(updates: [T, T][], upsert: boolean = false): DataModel<T> {
if(0 === updates.length) return this
updates.forEach(([ oldItem, newItem ]) => {
this.update(oldItem, newItem, upsert)
})
return this.fork()
}
private _create(item: T): DataModel<T> {
const record = { value: item }
this.records.add(record)
this.indexes.set(item, record)
return this
}
create(item: T): DataModel<T> {
return this._create(item).fork()
}
createMany(items: T[]): DataModel<T> {
items.forEach(this._create.bind(this))
return this.fork()
}
private _drop(item: T) {
const ref = this.indexes.get(item)
if(undefined === ref) return null
this.records.delete(ref)
this.indexes.delete(item)
return this
}
drop(item: T): DataModel<T> {
const ret = this._drop(item)
if(null === ret) return this
return this.fork()
}
dropMany(items: T[]): DataModel<T> {
const count = this.count()
const clone = this.fork()
items.forEach(clone._drop.bind(clone))
if(count === clone.count()) return this
return clone
}
}
export function Model<T extends { [key:string]: any }>(values: T[] = []): DataModel<T> {
return new DataModel(values)
}