import fs from 'fs'
import genPm from './genPm.mjs'
import errToStr from './_errToStr.mjs'
/**
* 使用stream寫入utf8文字至檔案
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/fsBuildWriteStreamText.test.mjs Github}
* @memberOf wsemi
* @returns {Object} 回傳函數物件,提供creat、write、end函數,create代表創建stream,可輸入fp為檔案路徑字串,回傳Promise,寫入完成(finish)時resolve,開檔或寫入失敗(含fp無效)時reject錯誤訊息字串,不同步拋出,該Promise已標記為已處理(呼叫端未接時不成為未處理之rejection,await者仍收到reject),write代表寫入字串,輸入c為字串(預設為行),須於create後呼叫(未create即呼叫則同步拋錯),create失敗後呼叫則不動作,end代表結束stream,無輸入,回傳Promise,於stream關閉(close)後resolve,未create或stream已關閉(如開檔失敗)時直接resolve
* @example
* need test in nodejs.
*
* let test = async () => {
*
* let ms = []
*
* let fdt = './_test_fsBuildWriteStreamText'
* fsCreateFolder(fdt) //創建臨時任務資料夾
*
* let fn = 't1.txt'
* let fp = `${fdt}/${fn}`
*
* let bdw = fsBuildWriteStreamText()
*
* let pm = bdw.create(fp)
* ms.push({ 'create': '' })
*
* bdw.write('abc')
* ms.push({ 'write': 'abc' })
* bdw.write('中文')
* ms.push({ 'write': '中文' })
* bdw.end()
*
* await pm
*
* let c = fs.readFileSync(fp, 'utf8')
* ms.push({ 'readFileSync': c })
*
* fsDeleteFolder(fdt) //刪除臨時任務資料夾
*
* console.log('ms', ms)
* return ms
* }
* await test()
* // ms [
* // { create: '' },
* // { write: 'abc' },
* // { write: '中文' },
* // { readFileSync: 'abc\n中文\n' }
* // ]
*
*/
function fsBuildWriteStreamText() {
//stream
let stream = null
//failed, 已呼叫create而串流失敗(createWriteStream同步拋錯或派發error), 之後write不動作
let failed = false
//create
let create = (fp) => {
//pm
let pm = genPm()
pm.catch(() => {}) //先標記已處理: 依範例流程(create→write→end→await pm)於await pm之前失敗(同步失敗即reject)或呼叫端未接時, 無此空catch即成為unhandledRejection(無處理器時行程以離開碼1結束); await pm者仍收到rejection
//failed
failed = false
//createWriteStream, fp無效(非字串等)時同步拋錯, 轉為reject
try {
stream = fs.createWriteStream(fp, { flags: 'w', encoding: 'utf8' })
}
catch (err) {
stream = null //無開啟中之串流, end直接resolve
failed = true
pm.reject(errToStr(err))
return pm
}
//finish
stream.on('finish', () => {
pm.resolve()
})
//error
stream.on('error', (err) => {
failed = true
pm.reject(errToStr(err)) //與fs系其他函數一致以字串reject
})
return pm
}
//write
let write = (c) => {
//check, 已呼叫create而失敗者不動作(原於同步失敗後對null拋TypeError, 遮蔽根因且使呼叫端跳過await pm); 未呼叫create即write仍對null同步拋TypeError(須於create後呼叫)
if (failed) {
return
}
stream.write(c + '\n')
}
//end
let end = () => {
let pmm = genPm()
//check, 未create或串流已關閉(如開檔失敗後已自行destroy並派發close)時close不會再派發, 原為永久懸置(未create時同步拋錯)
if (stream === null || stream.closed) {
pmm.resolve()
return pmm
}
stream.on('close', () => {
//close事件才代表檔案可刪除
pmm.resolve()
})
stream.end() //發出flush與close
return pmm
}
//r
let r = {
create,
write,
end,
}
return r
}
export default fsBuildWriteStreamText