Skip to content

Commit 593e671

Browse files
committed
feat(chrono): add to stage use-case
1 parent 20f1b36 commit 593e671

File tree

9 files changed

+180
-14
lines changed

9 files changed

+180
-14
lines changed

packages/chrono/playground/PlayApp.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ async function run() {
2121
if (command === 'cat') {
2222
console.log(await app.catFile(process.argv[3]))
2323
}
24+
25+
if (command === 'add') {
26+
console.log(await app.add(process.argv[3]))
27+
}
2428
}
2529

2630
run()

packages/chrono/src/ChronoApp.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import LocalObjectRepository from './repositories/implementations/LocalObjectRep
77
import CatFileUseCase from './use-cases/CatFileUseCase'
88
import HashFileUseCase from './use-cases/HashFileUseCase'
99
import InitUseCase from './use-cases/InitUseCase'
10+
import AddFileToStagedUseCase from './use-cases/AddFileToStagedUseCase'
11+
import LocalStageItemRepository from './repositories/implementations/LocalStageItemRepository'
12+
import IStageItemRepository from './repositories/IStageItemRepository'
1013

1114
export default class ChronoApp {
1215
private readonly objectRepository: IObjectRepository
@@ -15,6 +18,8 @@ export default class ChronoApp {
1518
private readonly objectTemporaryRepository: IObjectRepository
1619
private readonly blobTemporaryRepository: IBlobRepository
1720

21+
private readonly stageItemRepository: IStageItemRepository
22+
1823
constructor(
1924
private readonly drive: IDrive,
2025
private readonly hash: IHash
@@ -29,6 +34,8 @@ export default class ChronoApp {
2934
)
3035

3136
this.blobTemporaryRepository = new LocalBlobRepository(drive, hash, '.chrono/tmp/blobs')
37+
38+
this.stageItemRepository = new LocalStageItemRepository(drive)
3239
}
3340

3441
public async init() {
@@ -52,4 +59,15 @@ export default class ChronoApp {
5259

5360
return useCase.execute({ objectHash })
5461
}
62+
63+
public async add(path: string) {
64+
const useCase = new AddFileToStagedUseCase(
65+
this.drive,
66+
this.stageItemRepository,
67+
this.objectTemporaryRepository,
68+
this.blobTemporaryRepository
69+
)
70+
71+
return useCase.execute({ path })
72+
}
5573
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export default class ChronoStageItem {
2+
public type: string
3+
public path: string
4+
public hash: string
5+
6+
public static from({ type, path, hash }: ChronoStageItem) {
7+
const item = new ChronoStageItem()
8+
9+
item.type = type
10+
item.path = path
11+
item.hash = hash
12+
13+
return item
14+
}
15+
}

packages/chrono/src/exceptions/BaseException.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export default class BaseException extends Error {
2-
constructor(message: string, data?: any) {
2+
constructor(message = 'Some error happen', data?: any) {
33
super(message)
44

55
this.message = message
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import ChronoObject from '../entities/ChronoObject'
22

33
export default interface IObjectRepository {
4-
// exists(objectHash: string): Promise<boolean>
5-
find(objectHash: string): Promise<any>
4+
find(objectHash: string): Promise<ChronoObject | null>
65
save(object: ChronoObject): Promise<{ objectHash: string }>
76
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import ChronoStageItem from '../entities/ChronoStageItem'
2+
3+
export default interface IStageItemRepository {
4+
save(item: ChronoStageItem): Promise<void>
5+
findAll(): Promise<ChronoStageItem[]>
6+
}

packages/chrono/src/repositories/implementations/LocalObjectRepository.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import ChronoObject from '../../entities/ChronoObject'
2-
import ChronoObjectTree from '../../entities/ChronoObjectTree'
32
import IDrive from '../../gateways/IDrive'
43
import IHash from '../../gateways/IHash'
54
import IObjectRepository from '../IObjectRepository'
@@ -11,7 +10,7 @@ export default class LocalObjectRepository implements IObjectRepository {
1110
private readonly directory = '.chrono/objects'
1211
) {}
1312

14-
public async save(chronoObject: ChronoObject) {
13+
public save: IObjectRepository['save'] = async (chronoObject) => {
1514
const objectHash = await this.hash.hash(chronoObject.toBytes())
1615

1716
const startHash = objectHash.slice(0, 2)
@@ -31,7 +30,7 @@ export default class LocalObjectRepository implements IObjectRepository {
3130
}
3231
}
3332

34-
public async find(objectHash: string) {
33+
public find: IObjectRepository['find'] = async (objectHash) => {
3534
const startHash = objectHash.slice(0, 2)
3635
const endHash = objectHash.slice(2)
3736

@@ -59,10 +58,6 @@ export default class LocalObjectRepository implements IObjectRepository {
5958

6059
const chronoObject = new ChronoObject(bytes)
6160

62-
if (chronoObject.type === 'tree') {
63-
return new ChronoObjectTree(bytes)
64-
}
65-
6661
return chronoObject
6762
}
6863
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import ChronoStageItem from '../../entities/ChronoStageItem'
2+
import IDrive from '../../gateways/IDrive'
3+
import HelperService from '../../services/HelperService'
4+
import IStageItemRepository from '../IStageItemRepository'
5+
6+
export default class LocalStageItemRepository implements IStageItemRepository {
7+
constructor(
8+
private readonly drive: IDrive,
9+
private readonly filename = '.chrono/tmp/stage'
10+
) {}
11+
12+
public save: IStageItemRepository['save'] = async (item) => {
13+
const all = await this.findAll()
14+
15+
const search = all.find((i) => i.path === item.path)
16+
17+
if (!search) {
18+
all.push(item)
19+
}
20+
21+
if (search) {
22+
search.hash = item.hash
23+
}
24+
25+
const content = all.map((i) => `${i.type} ${i.hash} ${i.path}`).join('\n')
26+
27+
await this.drive.write(this.filename, HelperService.encode(content))
28+
}
29+
30+
public findAll: IStageItemRepository['findAll'] = async () => {
31+
const bytes = await this.drive.read(this.filename)
32+
33+
const items = [] as ChronoStageItem[]
34+
35+
if (bytes) {
36+
const content = HelperService.decode(bytes)
37+
38+
for (const line of content.split('\n')) {
39+
const [type, hash, path] = line.split(' ')
40+
41+
items.push(
42+
ChronoStageItem.from({
43+
type,
44+
hash,
45+
path,
46+
})
47+
)
48+
}
49+
}
50+
51+
return items
52+
}
53+
}
Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,91 @@
1+
import ChronoObjectTree from '../entities/ChronoObjectTree'
2+
import ChronoStageItem from '../entities/ChronoStageItem'
3+
import BaseException from '../exceptions/BaseException'
14
import IDrive from '../gateways/IDrive'
5+
import IBlobRepository from '../repositories/IBlobRepository'
6+
import IObjectRepository from '../repositories/IObjectRepository'
7+
import IStageItemRepository from '../repositories/IStageItemRepository'
8+
import HashFileUseCase from './HashFileUseCase'
29

310
interface Params {
411
path: string
512
}
613

714
export default class AddFileToStagedUseCase {
8-
constructor(private readonly drive: IDrive) {}
15+
private hashFileUseCase: HashFileUseCase
16+
17+
constructor(
18+
private readonly drive: IDrive,
19+
private readonly stageItemRepository: IStageItemRepository,
20+
private readonly objectRepository: IObjectRepository,
21+
private readonly blobRepository: IBlobRepository
22+
) {
23+
this.hashFileUseCase = new HashFileUseCase(drive, objectRepository, blobRepository)
24+
}
25+
26+
public async findAllTreeStageItem(rootPath: string) {
27+
const items = [] as ChronoStageItem[]
28+
29+
const { objectHash: rootHash } = await this.hashFileUseCase.execute({ path: rootPath })
30+
31+
const tree = (await this.objectRepository.find(rootHash)) as ChronoObjectTree
32+
33+
if (!tree || tree.type !== 'tree') {
34+
throw new BaseException()
35+
}
36+
37+
items.push(ChronoStageItem.from({ type: 'tree', hash: rootHash, path: rootPath }))
38+
39+
for (const entry of tree.entries) {
40+
const entryPath = `${rootPath}/${entry.name}`
41+
42+
if (entry.type === 'blob') {
43+
const { objectHash } = await this.hashFileUseCase.execute({ path: entryPath })
44+
45+
items.push(
46+
ChronoStageItem.from({ type: 'blob', hash: objectHash, path: entryPath })
47+
)
48+
}
49+
50+
if (entry.type === 'tree') {
51+
const subItems = await this.findAllTreeStageItem(entryPath)
52+
53+
items.push(...subItems)
54+
}
55+
}
56+
57+
return items
58+
}
959

1060
async execute({ path }: Params) {
11-
return path
12-
// create a staged object
13-
// use repository to save it
61+
const { objectHash } = await this.hashFileUseCase.execute({ path })
62+
63+
const chronoObject = await this.objectRepository.find(objectHash)
64+
65+
if (!chronoObject) {
66+
throw new BaseException()
67+
}
68+
69+
if (chronoObject.type === 'blob') {
70+
const item = ChronoStageItem.from({
71+
type: 'blob',
72+
hash: objectHash,
73+
path,
74+
})
75+
76+
await this.stageItemRepository.save(item)
77+
78+
return item
79+
}
80+
81+
if (chronoObject.type === 'tree') {
82+
const items = await this.findAllTreeStageItem(path)
83+
84+
for (const item of items) {
85+
await this.stageItemRepository.save(item)
86+
}
87+
88+
return items
89+
}
1490
}
1591
}

0 commit comments

Comments
 (0)