import fs from 'fs'
import fsDeleteFolderSafeCore from './fsDeleteFolderSafeCore.mjs'
import errToStr from './_errToStr.mjs'
/**
* 後端nodejs刪除資料夾,支援失敗重試機制
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/fsDeleteFolderSafe.test.mjs Github}
* @memberOf wsemi
* @param {String} pah 輸入欲刪除資料夾路徑字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Object} [opt.fs=fs] 輸入fs物件,須提供statSync、lstatSync、rmSync,預設為Node內建fs(供測試注入假fs);給null或缺少statSync時reject字串`opt.fs is invalid: ...`(不視為標的不存在),缺少其他函數時其呼叫之TypeError以字串reject
* @param {Integer} [opt.numRetry=5] 輸入嘗試刪除次數整數,預設5;遇EBUSY才重試,每次重試前等待300ms,最後一次嘗試失敗後不再等待而直接reject
* @param {Function} [opt.onTrace=null] 輸入觀察回調函數,於內部事件發生當下同步呼叫,傳入一個凍結之事件物件{ type, ... },供測試以事件(而非等待時間)同步與診斷;type為'attempt'時為即將呼叫rmSync嘗試刪除(含n為由1起算之嘗試序號),'retry'時為第n次嘗試遇EBUSY而即將等待ms毫秒(固定300)(含n、code為'EBUSY'、ms;僅在之後還有下一次嘗試時通知,故n小於numRetry),'done'時為第n次嘗試刪除成功而即將resolve(含n),'skip'時為前置檢查即結束而即將resolve或reject(含n為0與reason;reason為'not-exist'代表標的不存在而resolve、'not-folder'代表標的非資料夾而reject),'giveup'時為重試耗盡仍遇EBUSY而即將reject(含n為numRetry、code為'EBUSY';於最後一次嘗試失敗當下通知,不再等待),'fail'時為前置檢查拋錯或opt.fs不合(n為0,opt.fs不合時code為null)或第n次嘗試遇不可重試之錯誤而即將reject(含n、code為該錯誤之code字串,無則null);事件皆於resolve或reject之前通知;回調不帶接收者呼叫,其拋錯與回傳值皆忽略,不影響刪除,預設null
* @returns {Promise} 回傳Promise,resolve回傳成功訊息,reject回傳錯誤訊息字串(含重試耗盡仍被占用)
* @example
* need test in nodejs.
*
* let test = async () => {
*
* let ms = []
*
* let fdt = './_test_fsDeleteFolderSafe'
*
* fsCreateFolder(fdt) //創建臨時任務資料夾
*
* let rfl = fsCreateFile(`${fdt}/abc/abc.txt`, 'abc', { encoding: 'utf8' })
* console.log('rfl', rfl)
* ms.push({ 'fsCreateFile(before)': rfl })
*
* let rfd = fsCreateFolder(`${fdt}/def/mno`)
* console.log('rfd', rfd)
* ms.push({ 'fsCreateFolder(before)': rfd })
*
* let b1 = fsIsFile(`${fdt}/abc/abc.txt`)
* console.log('fsIsFile(before)', b1)
* ms.push({ 'fsIsFile(before)': b1 })
*
* let b2 = fsIsFolder(`${fdt}/def/mno`)
* console.log('fsIsFolder(before)', b2)
* ms.push({ 'fsIsFolder(before)': b2 })
*
* await fsDeleteFolderSafe(fdt)
*
* let b3 = fsIsFile(`${fdt}/abc/abc.txt`)
* console.log('fsIsFile(after)', b3)
* ms.push({ 'fsIsFile(after)': b3 })
*
* let b4 = fsIsFolder(`${fdt}/def/mno`)
* console.log('fsIsFolder(after)', b4)
* ms.push({ 'fsIsFolder(after)': b4 })
*
* console.log('ms', ms)
* return ms
* }
* test()
* // rfl { success: 'done: ./_test_fsDeleteFolderSafe/abc/abc.txt' }
* // rfd { success: 'done: ./_test_fsDeleteFolderSafe/def/mno' }
* // fsIsFile(before) true
* // fsIsFolder(before) true
* // fsIsFile(after) false
* // fsIsFolder(after) false
* // ms [
* // {
* // 'fsCreateFile(before)': { success: 'done: ./_test_fsDeleteFolderSafe/abc/abc.txt' }
* // },
* // {
* // 'fsCreateFolder(before)': { success: 'done: ./_test_fsDeleteFolderSafe/def/mno' }
* // },
* // { 'fsIsFile(before)': true },
* // { 'fsIsFolder(before)': true },
* // { 'fsIsFile(after)': false },
* // { 'fsIsFolder(after)': false }
* // ]
*
*/
async function fsDeleteFolderSafe(pah, opt = {}) {
//o, 展開置於try內: opt本身或其屬性不可讀(getter拋錯、已撤銷之Proxy)時展開即拋出, 於async函數內未捕捉即以Error物件reject
let o = null
try {
o = { fs, ...opt }
}
catch (err) {
return Promise.reject(errToStr(err))
}
return await fsDeleteFolderSafeCore(pah, o)
}
export default fsDeleteFolderSafe