import fs from 'fs'
import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import evem from './evem.mjs'
import genPm from './genPm.mjs'
import iseobj from './iseobj.mjs'
import haskey from './haskey.mjs'
import j2o from './j2o.mjs'
import o2j from './o2j.mjs'
import debounce from './debounce.mjs'
import ltdtDiffByKey from './ltdtDiffByKey.mjs'
import fsIsFolder from './fsIsFolder.mjs'
import fsCreateFolder from './fsCreateFolder.mjs'
import fsWatchFile from './fsWatchFile.mjs'
/**
* 後端nodejs偵測指定資料夾下之檔案變化,分來源與偵測端
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/fsTaskCp.test.mjs Github}
* @memberOf wsemi
* @param {String} fdSrc 輸入來源端更新紀錄之資料夾路徑字串
* @param {String} fdTar 輸入偵測端已紀錄之資料夾路徑字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @returns {Object} 回傳物件,包含buildSrc與buildTar函數,buildSrc回傳監聽事件物件ev, 可使用ev.set進行紀錄變更,buildTar回傳監聽事件物件,可監聽change事件,並使用接收事件資訊msg內的pm做為回傳執行成功與否狀態
* @example
* //need test in nodejs
*
* import fsDeleteFile from './src/fsDeleteFile.mjs'
* import fsCreateFolder from './src/fsCreateFolder.mjs'
* import fsDeleteFolder from './src/fsDeleteFolder.mjs'
* import fsTaskCp from './src/fsTaskCp.mjs'
*
* let test = async () => {
* return new Promise((resolve, reject) => {
* let msSrc = []
* let msTar = []
*
* let fp = './_taskcp'
* fsDeleteFolder(fp) //預先清除fsTaskCp持久化數據資料夾
*
* let fpc = './_taskcpSrc'
* fsDeleteFolder(fpc) //預先清除fsTaskCp持久化數據資料夾
*
* let fpt = './_taskcpTar'
* fsDeleteFolder(fpt) //預先清除fsTaskCp持久化數據資料夾
*
* let otk = fsTaskCp(fpc, fpt)
*
* let otkSrc = otk.buildSrc()
* otkSrc.on('set', (msg) => {
* console.log(`src send task...`, msg)
* msSrc.push({ type: 'src-send', msg: JSON.stringify(msg) })
* })
* otkSrc.on('remove', (msg) => {
* console.log(`src send task...`, msg)
* msSrc.push({ type: 'src-send', msg: JSON.stringify(msg) })
* })
*
* let otkTar = otk.buildTar()
* otkTar.on('change', (msg) => {
* // console.log('otkTar change', msg)
*
* //接收任務
* console.log(`tar recv task...`, msg.kpCmp)
* msTar.push({ type: 'tar-recv', msg: JSON.stringify(msg.kpCmp) })
*
* setTimeout(() => {
* console.log(`tar deal task done`)
* msTar.push({ type: 'tar-done', msg: JSON.stringify(msg.kpCmp) })
* msg.pm.resolve()
* }, 1000)
*
* })
*
* setTimeout(() => {
* fsCreateFolder(fp)
* }, 1)
*
* setTimeout(() => {
* fs.writeFileSync(`${fp}/abc.txt`, 'abc', 'utf8')
* otkSrc.set('abc.txt', 'hash_a1')
* }, 3000)
*
* setTimeout(() => {
* fs.writeFileSync(`${fp}/abc.txt`, 'mnop', 'utf8')
* otkSrc.set('abc.txt', 'hash_a2')
* fs.writeFileSync(`${fp}/def.txt`, 'def', 'utf8')
* otkSrc.set('def.txt', 'hash_b1')
* }, 6000)
*
* setTimeout(() => {
* fsDeleteFile(`${fp}/abc.txt`)
* otkSrc.remove('abc.txt')
* }, 9000)
*
* setTimeout(() => {
* otkTar.clear()
* }, 12000)
*
* setTimeout(() => {
* fsDeleteFolder(fp)
* fsDeleteFolder(fpc)
* fsDeleteFolder(fpt)
* let ms = {
* msSrc,
* msTar,
* }
* console.log('ms', ms)
* resolve(ms)
* }, 15000)
*
* })
* }
* await test()
* .catch((err) => {
* console.log(err)
* })
* // src send task... { type: 'set', fp: 'abc.txt', hash: 'hash_a1' }
* // tar recv task... {
* // del: [],
* // add: [ { fp: 'abc.txt', hash: 'hash_a1' } ],
* // same: [],
* // diff: []
* // }
* // tar deal task done
* // src send task... { type: 'set', fp: 'abc.txt', hash: 'hash_a2' }
* // src send task... { type: 'set', fp: 'def.txt', hash: 'hash_b1' }
* // tar recv task... {
* // del: [],
* // add: [ { fp: 'def.txt', hash: 'hash_b1' } ],
* // same: [],
* // diff: [ { fp: 'abc.txt', hash: 'hash_a2' } ]
* // }
* // src send task... { type: 'remove', fp: 'abc.txt' }
* // tar deal task done
* // tar recv task... {
* // del: [ { fp: 'abc.txt', hash: 'hash_a2' } ],
* // add: [],
* // same: [ { fp: 'def.txt', hash: 'hash_b1' } ],
* // diff: []
* // }
* // tar deal task done
* // ms {
* // msSrc: [
* // {
* // type: 'src-send',
* // msg: '{"type":"set","fp":"abc.txt","hash":"hash_a1"}'
* // },
* // {
* // type: 'src-send',
* // msg: '{"type":"set","fp":"abc.txt","hash":"hash_a2"}'
* // },
* // {
* // type: 'src-send',
* // msg: '{"type":"set","fp":"def.txt","hash":"hash_b1"}'
* // },
* // { type: 'src-send', msg: '{"type":"remove","fp":"abc.txt"}' }
* // ],
* // msTar: [
* // {
* // type: 'tar-recv',
* // msg: '{"del":[],"add":[{"fp":"abc.txt","hash":"hash_a1"}],"same":[],"diff":[]}'
* // },
* // {
* // type: 'tar-done',
* // msg: '{"del":[],"add":[{"fp":"abc.txt","hash":"hash_a1"}],"same":[],"diff":[]}'
* // },
* // {
* // type: 'tar-recv',
* // msg: '{"del":[],"add":[{"fp":"def.txt","hash":"hash_b1"}],"same":[],"diff":[{"fp":"abc.txt","hash":"hash_a2"}]}'
* // },
* // {
* // type: 'tar-done',
* // msg: '{"del":[],"add":[{"fp":"def.txt","hash":"hash_b1"}],"same":[],"diff":[{"fp":"abc.txt","hash":"hash_a2"}]}'
* // },
* // {
* // type: 'tar-recv',
* // msg: '{"del":[{"fp":"abc.txt","hash":"hash_a2"}],"add":[],"same":[{"fp":"def.txt","hash":"hash_b1"}],"diff":[]}'
* // },
* // {
* // type: 'tar-done',
* // msg: '{"del":[{"fp":"abc.txt","hash":"hash_a2"}],"add":[],"same":[{"fp":"def.txt","hash":"hash_b1"}],"diff":[]}'
* // }
* // ]
* // }
*
*/
function fsTaskCp(fdSrc, fdTar, opt = {}) {
//check fdSrc
// let fdSrc = get(opt, 'fdSrc', '')
// if (!isestr(fdSrc)) {
// fdSrc = './_taskcpSrc'
// }
if (!fsIsFolder(fdSrc)) {
fsCreateFolder(fdSrc)
}
//check fdTar
// let fdTar = get(opt, 'fdTar', '')
// if (!isestr(fdTar)) {
// fdTar = './_taskcpTar'
// }
if (!fsIsFolder(fdTar)) {
fsCreateFolder(fdTar)
}
//fpHashSrc, fpHashTar
let fpHashSrc = `${fdSrc}/fpHash.json`
let fpHashTar = `${fdTar}/fpHash.json`
//_readKpOb
let _readKpOb = (fpHash) => {
let r = {} //儲存格式為物件
try {
let j = fs.readFileSync(fpHash, 'utf8')
r = j2o(j)
}
catch (err) {}
return r
}
//_writeKpOb
let _writeKpOb = (fpHash, kp) => {
try {
let j = o2j(kp)
fs.writeFileSync(fpHash, j, 'utf8')
}
catch (err) {}
}
//_getOb
let _getOb = (fpHash, fp) => {
//_readKpOb
let kpOb = _readKpOb(fpHash)
// console.log('kpOb', kpOb)
//r
let r = get(kpOb, fp, '')
// console.log(fp, r)
return r
}
//_setOb
let _setOb = (fpHash, fp, hash) => {
//_readKpOb
let kpOb = _readKpOb(fpHash)
// console.log('kpOb', kpOb)
//save
kpOb[fp] = hash
//_writeKpOb
_writeKpOb(fpHash, kpOb)
}
//_removeOb
let _removeOb = (fpHash, fp) => {
//_readKpOb
let kpOb = _readKpOb(fpHash)
// console.log('kpOb', kpOb)
//delete
if (haskey(kpOb, fp)) {
delete kpOb[fp]
}
//_writeKpOb
_writeKpOb(fpHash, kpOb)
}
let readKpObSrc = () => {
return _readKpOb(fpHashSrc)
}
let writeKpObSrc = (kp) => {
return _writeKpOb(fpHashSrc, kp)
}
let getObSrc = (fp) => {
return _getOb(fpHashSrc, fp)
}
let setObSrc = (fp, hash) => {
return _setOb(fpHashSrc, fp, hash)
}
let removeObSrc = (fp) => {
return _removeOb(fpHashSrc, fp)
}
let readKpObTar = () => {
return _readKpOb(fpHashTar)
}
let writeKpObTar = (kp) => {
return _writeKpOb(fpHashTar, kp)
}
let getObTar = (fp) => {
return _getOb(fpHashTar, fp)
}
let setObTar = (fp, hash) => {
return _setOb(fpHashTar, fp, hash)
}
let removeObTar = (fp) => {
return _removeOb(fpHashTar, fp)
}
let cvKpToLtdt = (kp) => {
let ltdt = []
each(kp, (v, k) => {
ltdt.push({ fp: k, hash: v })
})
return ltdt
}
//buildSrc
let buildSrc = () => {
//紀錄檔案變更至fpHashSrc, 供buildTar偵測驅動使用
//ev
let ev = evem()
//_set
let _set = (fp, hash) => {
setObSrc(fp, hash)
ev.emit('set', { type: 'set', fp, hash })
}
//_remove
let _remove = (fp) => {
removeObSrc(fp)
ev.emit('remove', { type: 'remove', fp })
}
//save
ev.set = _set
ev.get = getObSrc
ev.remove = _remove
ev.readKp = readKpObSrc
ev.writeKp = writeKpObSrc
return ev
}
//buildTar
let buildTar = () => {
//讀取對方紀錄fpHashSrc, 讀取自己備份紀錄fpHashTar, 偵測差異後emit觸發事件使用
//ev
let ev = evem()
//dbc
let dbc = debounce(300)
//core
let core = async (msg) => {
// console.log('core', msg)
//pm
let pm = genPm()
//kpSrc
let kpSrc = readKpObSrc()
//check
if (!iseobj(kpSrc)) {
//來源無紀錄檔案, 故不偵測
pm.resolve('no src')
return pm
}
//kpTar
let kpTar = readKpObTar()
//ltdtSrc, ltdtTar
let ltdtSrc = cvKpToLtdt(kpSrc)
let ltdtTar = cvKpToLtdt(kpTar)
// console.log('kpSrc', kpSrc)
// console.log('kpTar', kpTar)
//ltdtDiffByKey
let r = ltdtDiffByKey(ltdtTar, ltdtSrc, 'fp', { withInfor: false })
// console.log('ltdtDiffByKey', r)
//pmm, 供外部提供任務執行狀態
let pmm = genPm()
pmm
.then((res) => {
// console.log('執行任務成功, 更新紀錄檔案', kpSrc)
writeKpObTar(kpSrc)
})
.catch(() => {
//執行任務失敗, 不更新紀錄檔
})
//emit
ev.emit('change', { kpSrc, kpTar, kpCmp: r, pm: pmm })
return pm
}
//evFile
let evFile = fsWatchFile(fpHashSrc)
evFile.on('change', (msg) => {
// console.log(msg.type, getFileName(msg.fp))
dbc(() => {
core(msg)
.catch(() => {})
})
})
//save
ev.set = setObTar
ev.get = getObTar
ev.remove = removeObTar
ev.readKp = readKpObTar
ev.writeKp = writeKpObTar
ev.clear = evFile.clear
return ev
}
//r
let r = {
buildSrc,
buildTar,
}
return r
}
export default fsTaskCp