-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathtree-builder.ts
124 lines (101 loc) · 3.2 KB
/
tree-builder.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
import { DirFlat } from './dir-flat.js'
import { Dir } from './dir.js'
import { flatToShard } from './flat-to-shard.js'
import { toPathComponents } from './utils/to-path-components.js'
import type { ImportResult, InProgressImportResult, TreeBuilder, WritableStorage } from './index.js'
import type { PersistOptions } from './utils/persist.js'
export interface AddToTreeOptions extends PersistOptions {
shardSplitThresholdBytes: number
shardFanoutBits: number
}
async function addToTree (elem: InProgressImportResult, tree: Dir, options: AddToTreeOptions): Promise<Dir> {
const pathElems = toPathComponents(elem.path ?? '')
const lastIndex = pathElems.length - 1
let parent = tree
let currentPath = ''
for (let i = 0; i < pathElems.length; i++) {
const pathElem = pathElems[i]
currentPath += `${currentPath !== '' ? '/' : ''}${pathElem}`
const last = (i === lastIndex)
parent.dirty = true
parent.cid = undefined
parent.size = undefined
if (last) {
await parent.put(pathElem, elem)
tree = await flatToShard(null, parent, options.shardSplitThresholdBytes, options)
} else {
let dir = await parent.get(pathElem)
if ((dir == null) || !(dir instanceof Dir)) {
dir = new DirFlat({
root: false,
dir: true,
parent,
parentKey: pathElem,
path: currentPath,
dirty: true,
flat: true,
mtime: dir?.unixfs?.mtime,
mode: dir?.unixfs?.mode
}, options)
}
await parent.put(pathElem, dir)
parent = dir
}
}
return tree
}
async function * flushAndYield (tree: Dir | InProgressImportResult, blockstore: WritableStorage): AsyncGenerator<ImportResult> {
if (!(tree instanceof Dir)) {
if (tree.unixfs?.isDirectory() === true) {
yield tree
}
return
}
yield * tree.flush(blockstore)
}
export interface TreeBuilderOptions extends AddToTreeOptions {
wrapWithDirectory: boolean
}
export function defaultTreeBuilder (options: TreeBuilderOptions): TreeBuilder {
return async function * treeBuilder (source, block) {
let tree: Dir = new DirFlat({
root: true,
dir: true,
path: '',
dirty: true,
flat: true
}, options)
let rootDir: string | undefined
let singleRoot = false
for await (const entry of source) {
if (entry == null) {
continue
}
// if all paths are from the same root directory, we should
// wrap them all in that root directory
const dir = `${entry.originalPath ?? ''}`.split('/')[0]
if (dir != null && dir !== '') {
if (rootDir == null) {
rootDir = dir
singleRoot = true
} else if (rootDir !== dir) {
singleRoot = false
}
}
tree = await addToTree(entry, tree, options)
if (entry.unixfs?.isDirectory() !== true) {
yield entry
}
}
if (options.wrapWithDirectory || (singleRoot && tree.childCount() > 1)) {
yield * flushAndYield(tree, block)
} else {
for await (const unwrapped of tree.eachChildSeries()) {
if (unwrapped == null) {
continue
}
yield * flushAndYield(unwrapped.child, block)
}
}
}
}