mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-08 03:20:12 +08:00
Feat filegraph (#194)
* feat: update filegraph on touch command * feat: update filegraph on mkdir * feat: add different parsing opts for each command * refactor: replace axios with fetch. put handleCommand to a new file * feat: add config for mounting cli in dev stage * refactor: clean up unused validators * feat: add command handler for copy files. reuse resolvePath function * feat: add rm handler, support specific files deletion. Glob not supported yet * feat: handle discover cases that the folder is actually empty * feat: handle discover empty folder better and support rmdir * feat: handle mv command * refactor: remove unused console logs
This commit is contained in:
1 parent
a732c1d119
commit
f0a09ac4d9
19 files changed
+326
-119
No files matched your search
@@ -4,6 +4,7 @@ export interface FileObject {
|
||||
path: string
|
||||
type: 'file' | 'directory'
|
||||
discovered: boolean
|
||||
empty?: boolean
|
||||
}
|
||||
|
||||
export default class File implements FileObject {
|
||||
@@ -11,12 +12,14 @@ export default class File implements FileObject {
|
||||
name: string
|
||||
type: 'file' | 'directory'
|
||||
discovered: boolean
|
||||
empty?: boolean
|
||||
|
||||
constructor(obj: FileObject) {
|
||||
this.path = obj.path.trimEnd()
|
||||
this.name = basename(obj.path)
|
||||
this.type = obj.type
|
||||
this.discovered = obj.discovered
|
||||
this.empty = obj.empty && obj.type === 'directory'
|
||||
}
|
||||
|
||||
isDirectory() {
|
||||
|
||||
@@ -45,17 +45,14 @@ export default class FileGraph extends FileNode {
|
||||
|
||||
handleEvent(event: FileGraphUpdateEvent) {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (true) {
|
||||
case !!event.add:
|
||||
this.add(event.add)
|
||||
break
|
||||
case !!event.remove:
|
||||
this.remove(event.remove)
|
||||
break
|
||||
case !!event.discover:
|
||||
this.discover(event.discover)
|
||||
break
|
||||
// Implement other cases if needed
|
||||
if (event.add) {
|
||||
this.add(event.add)
|
||||
}
|
||||
if (event.remove) {
|
||||
this.remove(event.remove)
|
||||
}
|
||||
if (event.discover) {
|
||||
this.discover(event.discover)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
import File, { FileObject } from './file.js'
|
||||
import {
|
||||
DuplicateItemError,
|
||||
ParentNotExistsError,
|
||||
FileNotExistsError,
|
||||
} from './errors.js'
|
||||
import { ParentNotExistsError, FileNotExistsError } from './errors.js'
|
||||
import { dirname } from './utils.js'
|
||||
|
||||
export interface IFileNode {
|
||||
@@ -29,15 +25,24 @@ export default class FileNode extends File implements IFileNode {
|
||||
) {
|
||||
const file = new FileNode(fileInput)
|
||||
if (file.path === this.path) {
|
||||
throw new DuplicateItemError(
|
||||
`File ${file.path} already exists in the tree: ${this.toString()}`
|
||||
)
|
||||
if (!this.discovered) {
|
||||
this.discovered = file.discovered
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (this.children.some((child) => child.path === file.path)) {
|
||||
throw new DuplicateItemError(
|
||||
`File ${file.path} already exists in the tree: ${this.toString()}`
|
||||
)
|
||||
let existing = false
|
||||
for (const child of this.children) {
|
||||
if (child.path === file.path) {
|
||||
existing = true
|
||||
if (!child.discovered) {
|
||||
child.discovered = file.discovered
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (existing) {
|
||||
return
|
||||
}
|
||||
|
||||
const parentPath = dirname(file.path)
|
||||
@@ -48,7 +53,7 @@ export default class FileNode extends File implements IFileNode {
|
||||
// Recursively call addChild on the correct child node
|
||||
const childNode = this.children.find((child) => child.contains(file))
|
||||
if (childNode) {
|
||||
childNode.addChild(file)
|
||||
childNode.addChild(file, { makeParents })
|
||||
} else if (makeParents) {
|
||||
const parent = new FileNode({
|
||||
path: parentPath,
|
||||
@@ -95,6 +100,12 @@ export default class FileNode extends File implements IFileNode {
|
||||
// Update the state of this node
|
||||
this.discovered = this.discovered || fileNode.discovered
|
||||
|
||||
// the provided directory is empty, remove all children
|
||||
if (fileNode.empty) {
|
||||
this.children = []
|
||||
return
|
||||
}
|
||||
|
||||
fileNode.children.forEach((child) => {
|
||||
const existingChild = this.children.find((c) => c.path === child.path)
|
||||
if (existingChild) {
|
||||
@@ -140,4 +151,11 @@ export default class FileNode extends File implements IFileNode {
|
||||
.join('')
|
||||
return `${name}\n${childrenString}`
|
||||
}
|
||||
|
||||
include(filePath: string, type?: 'file' | 'directory'): boolean {
|
||||
if (this.path === filePath) {
|
||||
return type ? this.type === type : true
|
||||
}
|
||||
return this.children.some((child) => child.include(filePath, type))
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,44 @@ describe('FileGraph', () => {
|
||||
type: 'directory',
|
||||
discovered: true,
|
||||
},
|
||||
{
|
||||
path: '/home/user/folder2/file2.txt',
|
||||
type: 'file',
|
||||
discovered: true,
|
||||
},
|
||||
]
|
||||
|
||||
fileGraph.add(filesToAdd)
|
||||
|
||||
expect(fileGraph.children.length).toBe(1)
|
||||
expect(fileGraph.children[0].children[0].children.length).toBe(2)
|
||||
expect(fileGraph.children[0].children[0].children.length).toBe(3)
|
||||
})
|
||||
|
||||
test('ignore files that already exist', () => {
|
||||
const fileGraph = new FileGraph(root)
|
||||
|
||||
const filesToAdd: FileObject[] = [
|
||||
{
|
||||
path: '/home/user/folder1',
|
||||
type: 'directory',
|
||||
discovered: true,
|
||||
},
|
||||
{
|
||||
path: '/home/user/folder1/file1.txt',
|
||||
type: 'file',
|
||||
discovered: true,
|
||||
},
|
||||
]
|
||||
|
||||
fileGraph.add(filesToAdd)
|
||||
|
||||
expect(fileGraph.children.length).toBe(1)
|
||||
expect(fileGraph.children[0].children[0].children.length).toBe(1)
|
||||
|
||||
fileGraph.add([filesToAdd[1]])
|
||||
|
||||
expect(fileGraph.children.length).toBe(1)
|
||||
expect(fileGraph.children[0].children[0].children.length).toBe(1)
|
||||
})
|
||||
|
||||
test('remove files from graph', () => {
|
||||
@@ -175,4 +207,36 @@ describe('FileGraph', () => {
|
||||
3
|
||||
)
|
||||
})
|
||||
|
||||
test('discover a empty directory which children just being deleted', () => {
|
||||
const fileGraph = new FileGraph(root)
|
||||
const discoverFiles = [
|
||||
{
|
||||
path: '/home/user/folder1',
|
||||
type: 'directory',
|
||||
discovered: true,
|
||||
},
|
||||
{
|
||||
path: '/home/user/folder1/file1.txt',
|
||||
type: 'file',
|
||||
discovered: true,
|
||||
},
|
||||
]
|
||||
|
||||
fileGraph.add(discoverFiles as FileObject[])
|
||||
expect(fileGraph.include('/home/user/folder1/file1.txt', 'file')).toBe(true)
|
||||
|
||||
fileGraph.discover([
|
||||
{
|
||||
path: '/home/user/folder1',
|
||||
type: 'directory',
|
||||
discovered: true,
|
||||
empty: true,
|
||||
},
|
||||
])
|
||||
|
||||
expect(fileGraph.include('/home/user/folder1/file1.txt', 'file')).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, test, expect } from '@jest/globals'
|
||||
import { DuplicateItemError, ParentNotExistsError } from '../errors.js'
|
||||
import { ParentNotExistsError } from '../errors.js'
|
||||
import FileNode, { IFileNode } from '../fileNode.js'
|
||||
import { FileObject } from '../file.js'
|
||||
|
||||
@@ -47,11 +47,11 @@ describe('FileNode', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('should throw error when adding duplicate item', () => {
|
||||
test('should override discovered when adding duplicate item', () => {
|
||||
const child: FileObject = {
|
||||
path: '/home/rudeus/test',
|
||||
type: 'directory',
|
||||
discovered: true,
|
||||
discovered: false,
|
||||
}
|
||||
|
||||
// Adding the child for the first time
|
||||
@@ -60,9 +60,10 @@ describe('FileNode', () => {
|
||||
expect(rootNode.children.length).toBe(1)
|
||||
|
||||
// Trying to add the same child again should throw an error
|
||||
expect(() => {
|
||||
rootNode.addChild(child)
|
||||
}).toThrow(DuplicateItemError)
|
||||
child.discovered = true
|
||||
rootNode.addChild(child)
|
||||
|
||||
expect(rootNode.children[0].discovered).toBe(true)
|
||||
})
|
||||
|
||||
test('should throw error when parent does not exist', () => {
|
||||
|
||||
Reference in new issue
Block a user