generated from fission-codes/project-template
-
Notifications
You must be signed in to change notification settings - Fork 24
/
tree.ts
157 lines (125 loc) · 4.4 KB
/
tree.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import type { CID } from "multiformats/cid"
import * as Check from "../types/check.js"
import * as Depot from "../../components/depot/implementation.js"
import * as Protocol from "../protocol/index.js"
import * as Link from "../link.js"
import { HardLinks, BaseLinks, Tree, File, Puttable, UpdateCallback, PuttableUnixTree } from "../types.js"
import { Maybe, decodeCID } from "../../common/index.js"
import { isObject, hasProp } from "../../common/type-checks.js"
import { Segments as Path } from "../../path/index.js"
import BareFile from "../bare/file.js"
import BaseTree from "../base/tree.js"
class BareTree extends BaseTree {
depot: Depot.Implementation
links: HardLinks
children: { [ name: string ]: Tree | File }
type: "BareTree"
constructor(depot: Depot.Implementation, links: HardLinks) {
super()
this.type = "BareTree"
this.links = links
this.children = {}
this.depot = depot
}
static async empty(depot: Depot.Implementation): Promise<BareTree> {
return new BareTree(depot, {})
}
static async fromCID(depot: Depot.Implementation, cid: CID): Promise<BareTree> {
const links = Link.arrToMap(
(await depot.getUnixDirectory(cid))
)
return new BareTree(depot, links)
}
static fromLinks(depot: Depot.Implementation, links: HardLinks): BareTree {
return new BareTree(depot, links)
}
static instanceOf(obj: unknown): obj is BareTree {
return isObject(obj)
&& hasProp(obj, "links")
&& hasProp(obj, "children")
&& hasProp(obj, "type")
&& obj.type === "BareTree"
}
async createChildTree(name: string, onUpdate: Maybe<UpdateCallback>): Promise<Tree> {
const child = await BareTree.empty(this.depot)
const existing = this.children[ name ]
if (existing) {
if (Check.isFile(existing)) {
throw new Error(`There is a file at the given path: ${name}`)
}
return existing
}
await this.updateDirectChild(child, name, onUpdate)
return child
}
async createOrUpdateChildFile(content: Uint8Array, name: string, onUpdate: Maybe<UpdateCallback>): Promise<BareFile> {
const existing = await this.getDirectChild(name)
let file: BareFile
if (existing === null) {
file = await BareFile.create(this.depot, content)
} else if (BareFile.instanceOf(existing)) {
file = await existing.updateContent(content)
} else {
throw new Error(`There is already a directory with that name: ${name}`)
}
await this.updateDirectChild(file, name, onUpdate)
return file
}
async putDetailed(): Promise<Depot.PutResult> {
return Protocol.basic.putLinks(this.depot, this.links)
}
async putAndUpdateLink(child: Puttable, name: string, onUpdate: Maybe<UpdateCallback>): Promise<this> {
const details = await child.putDetailed()
this.updateLink(name, details)
onUpdate && await onUpdate()
return this
}
async updateDirectChild(child: Tree | File, name: string, onUpdate: Maybe<UpdateCallback>): Promise<this> {
this.children[ name ] = child
return this.putAndUpdateLink(child, name, onUpdate)
}
removeDirectChild(name: string): this {
delete this.links[ name ]
if (this.children[ name ]) {
delete this.children[ name ]
}
return this
}
async getDirectChild(name: string): Promise<Tree | File | null> {
if (this.children[ name ]) {
return this.children[ name ]
}
const link = this.links[ name ] || null
if (link === null) return null
const cid = decodeCID(link.cid)
const child = link.isFile
? await BareFile.fromCID(this.depot, cid)
: await BareTree.fromCID(this.depot, cid)
// check that the child wasn't added while retrieving the content from the network
if (this.children[ name ]) {
return this.children[ name ]
}
this.children[ name ] = child
return child
}
async get(path: Path): Promise<Tree | File | null> {
const [ head, ...nextPath ] = path
if (!head) return this
const nextTree = await this.getDirectChild(head)
if (!nextPath.length) {
return nextTree
} else if (nextTree === null || Check.isFile(nextTree)) {
return null
}
return nextTree.get(nextPath)
}
updateLink(name: string, result: Depot.PutResult): this {
const { cid, size, isFile } = result
this.links[ name ] = Link.make(name, cid, isFile, size)
return this
}
getLinks(): BaseLinks {
return this.links
}
}
export default BareTree