mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-06 03:20:13 +08:00
feat: add merge function
Able to deal with: 1. merge a subtree
This commit is contained in:
1 parent
1778780364
commit
a66de4dfe1
5 files changed
+114
-5
No files matched your search
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "File Graph Yarn Jest",
|
||||||
|
"runtimeExecutable": "yarn",
|
||||||
|
"args": ["test", "--runInBand"],
|
||||||
|
"cwd": "${workspaceFolder}/packages/file-graph",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import { basename } from './utils'
|
||||||
|
|
||||||
export default class File {
|
export default class File {
|
||||||
constructor({ path, name, type, discovered }) {
|
constructor({ path, type, discovered }) {
|
||||||
this.path = path
|
this.path = path
|
||||||
this.name = name
|
this.name = basename(path)
|
||||||
this.type = type
|
this.type = type
|
||||||
this.discovered = discovered
|
this.discovered = discovered
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,15 @@ export default class FileNode extends File {
|
|||||||
addChild(fileInput, { makeParents } = {}) {
|
addChild(fileInput, { makeParents } = {}) {
|
||||||
const file = new FileNode(fileInput)
|
const file = new FileNode(fileInput)
|
||||||
if (file.path === this.path) {
|
if (file.path === this.path) {
|
||||||
throw new DuplicateItemError('File already exists in the tree')
|
throw new DuplicateItemError(
|
||||||
|
`File ${file.path} already exists in the tree: ${this.toString()}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.children.some((child) => child.path === file.path)) {
|
if (this.children.some((child) => child.path === file.path)) {
|
||||||
throw new DuplicateItemError('File already exists in the tree')
|
throw new DuplicateItemError(
|
||||||
|
`File ${file.path} already exists in the tree: ${this.toString()}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentPath = dirname(file.path)
|
const parentPath = dirname(file.path)
|
||||||
@@ -37,11 +41,47 @@ export default class FileNode extends File {
|
|||||||
})
|
})
|
||||||
this.addChild(parent, { makeParents })
|
this.addChild(parent, { makeParents })
|
||||||
} else {
|
} else {
|
||||||
throw new ParentNotExistsError('Parent path not found in the tree')
|
throw new ParentNotExistsError(
|
||||||
|
`Parent path not found in the tree, ${file.path}, ${this.path}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
merge(fileNode) {
|
||||||
|
// Merge the children of the provided file node into this node
|
||||||
|
// If a child already exists, merge the children of that child
|
||||||
|
// If a child does not exist, add it to this node
|
||||||
|
if (fileNode.path === this.path) {
|
||||||
|
fileNode.children.forEach((child) => {
|
||||||
|
const existingChild = this.children.find((c) => c.path === child.path)
|
||||||
|
if (existingChild) {
|
||||||
|
existingChild.merge(child)
|
||||||
|
} else {
|
||||||
|
this.children.push(child)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// If a child exists in this node but not in the provided node, remove it
|
||||||
|
this.children = this.children.filter((child) =>
|
||||||
|
fileNode.children.some((c) => c.path === child.path)
|
||||||
|
)
|
||||||
|
} else if (this.contains(fileNode)) {
|
||||||
|
const childNode = this.children.find((child) => child.contains(fileNode))
|
||||||
|
if (childNode) {
|
||||||
|
childNode.merge(fileNode)
|
||||||
|
} else {
|
||||||
|
this.addChild(fileNode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'Cannot merge file nodes with different paths',
|
||||||
|
this.path,
|
||||||
|
fileNode.path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
const indent = ' '.repeat(this.path.split('/').length - 1)
|
const indent = ' '.repeat(this.path.split('/').length - 1)
|
||||||
const suffix = this.isDirectory() ? '/' : ''
|
const suffix = this.isDirectory() ? '/' : ''
|
||||||
|
|||||||
@@ -93,4 +93,53 @@ describe('FileNode', () => {
|
|||||||
currentNode = currentNode.children[0]
|
currentNode = currentNode.children[0]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('merge-two-trees', () => {
|
||||||
|
const root = {
|
||||||
|
path: '/',
|
||||||
|
type: 'folder',
|
||||||
|
}
|
||||||
|
const files1 = [
|
||||||
|
{ path: '/a', type: 'file' },
|
||||||
|
{ path: '/b', type: 'folder' },
|
||||||
|
{ path: '/b/c', type: 'folder' },
|
||||||
|
{ path: '/b/d', type: 'file' },
|
||||||
|
{ path: '/b/c/f', type: 'file' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const files2 = [
|
||||||
|
{ path: '/b', type: 'folder' },
|
||||||
|
{ path: '/b/c', type: 'folder' },
|
||||||
|
{ path: '/b/e', type: 'file' },
|
||||||
|
{ path: '/b/c/g', type: 'file' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ path: '/a', type: 'file' },
|
||||||
|
{ path: '/b', type: 'folder' },
|
||||||
|
{ path: '/b/c', type: 'folder' },
|
||||||
|
{ path: '/b/e', type: 'file' },
|
||||||
|
{ path: '/b/c/g', type: 'file' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const node1 = new FileNode(root)
|
||||||
|
files1.forEach((file) => node1.addChild(file))
|
||||||
|
|
||||||
|
const node2 = new FileNode(files2[0])
|
||||||
|
files2.slice(1).forEach((file) => node2.addChild(file))
|
||||||
|
|
||||||
|
console.log('node1', node1.toString())
|
||||||
|
console.log('node2', node2.toString())
|
||||||
|
|
||||||
|
const expectedNode = new FileNode(root)
|
||||||
|
expected.forEach((file) => expectedNode.addChild(file))
|
||||||
|
|
||||||
|
console.log('expected', expectedNode.toString())
|
||||||
|
|
||||||
|
node1.merge(node2)
|
||||||
|
|
||||||
|
console.log('node1-after', node1.toString())
|
||||||
|
|
||||||
|
expect(node1.toString()).toBe(expectedNode.toString())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
export function dirname(path) {
|
export function dirname(path) {
|
||||||
const parts = path.split('/')
|
const parts = path.split('/')
|
||||||
|
if (parts.length <= 2) {
|
||||||
|
return '/'
|
||||||
|
}
|
||||||
parts.pop()
|
parts.pop()
|
||||||
return parts.join('/')
|
return parts.join('/')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user