ionian/src/diff/content_handler.ts
2022-06-21 14:42:51 +09:00

102 lines
No EOL
3.9 KiB
TypeScript

import {basename, dirname, join as pathjoin} from 'path';
import {Document, DocumentAccessor} from '../model/mod';
import { ContentFile, createContentFile } from '../content/mod';
import {IDiffWatcher} from './watcher';
import {ContentList} from './content_list';
//refactoring needed.
export class ContentDiffHandler{
/** content file list waiting to add */
waiting_list:ContentList;
/** deleted contents */
tombstone: Map<string,Document>;//hash, contentfile
doc_cntr: DocumentAccessor;
/** content type of handle */
content_type: string;
constructor(cntr: DocumentAccessor,content_type:string){
this.waiting_list = new ContentList();
this.tombstone = new Map<string,Document>();
this.doc_cntr = cntr;
this.content_type = content_type;
}
async setup(){
const deleted = await this.doc_cntr.findDeleted(this.content_type);
for (const it of deleted) {
this.tombstone.set(it.content_hash,it);
}
}
register(diff:IDiffWatcher){
diff.on('create',(path)=>this.OnCreated(path))
.on('delete',(path)=>this.OnDeleted(path))
.on('change',(prev,cur)=>this.OnChanged(prev,cur));
}
private async OnDeleted(cpath: string){
const basepath = dirname(cpath);
const filename = basename(cpath);
console.log("deleted ",cpath);
//if it wait to add, delete it from waiting list.
if(this.waiting_list.hasByPath(cpath)){
this.waiting_list.deleteByPath(cpath);
return;
}
const dbc = await this.doc_cntr.findByPath(basepath,filename);
//when there is no related content in db, ignore.
if(dbc.length === 0) return;
const content_hash = dbc[0].content_hash;
// When a path is changed, it takes into account when the
// creation event occurs first and the deletion occurs, not
// the change event.
const cf = this.waiting_list.getByHash(content_hash);
if(cf){
//if a path is changed, update the changed path.
console.log("update path from", cpath,"to",cf.path);
const newFilename = basename(cf.path);
const newBasepath = dirname(cf.path);
await this.waiting_list.deleteByHash(content_hash);
await this.doc_cntr.update({
id:dbc[0].id,
deleted_at: null,
filename:newFilename,
basepath:newBasepath,
});
return;
}
//invalidate db and add it to tombstone.
await this.doc_cntr.update({
id:dbc[0].id,
deleted_at: Date.now(),
});
this.tombstone.set(dbc[0].content_hash, dbc[0]);
}
private async OnCreated(cpath:string){
const basepath = dirname(cpath);
const filename = basename(cpath);
console.log("createContentFile", cpath);
const content = createContentFile(this.content_type,pathjoin(basepath,filename));
const hash = await content.getHash();
const c = this.tombstone.get(hash);
if(c !== undefined){
this.doc_cntr.update({
id: c.id,
deleted_at: null,
filename:filename,
basepath:basepath
});
return;
}
if(this.waiting_list.hasByHash(hash)){
console.log("Conflict!!!");
}
this.waiting_list.set(content);
}
private async OnChanged(prev_path:string,cur_path:string){
const prev_basepath = dirname(prev_path);
const prev_filename = basename(prev_path);
const cur_basepath = dirname(cur_path);
const cur_filename = basename(cur_path);
const doc = await this.doc_cntr.findByPath(prev_basepath,prev_filename);
await this.doc_cntr.update({...doc[0],
basepath:cur_basepath,
filename:cur_filename});
}
}