# WJsonStream.mjs

    // import intoStream from 'into-stream'
    import intoStream from './intoStream.mjs' //into-stream的index.js內實際是es6的module, mocha識別js為用require讀故會報錯, 另外fork出來改寫
    // import JSONStream from 'JSONStream'
    import JSONStream from './JSONStream.mjs' //through內無法轉換stream, 故只好fork出來改寫, 且只改寫parse函數
    // import JsonStreamStringify from 'json-stream-stringify'
    // import JsonStreamStringify from 'json-stream-stringify/lib/umd.js'
    // import JsonStreamStringify from 'json-stream-stringify/lib/umd.polyfill.js'
    import JsonStreamStringify from './JsonStreamStringify.mjs' //json-stream-stringify需使用umd.polyfill否則會出現setImmediate is not defined, 也無法通過node polyfill修改, 另外fork出來改寫
    import get from 'lodash-es/get.js'
    import isNumber from 'lodash-es/isNumber.js'
    import isestr from 'wsemi/src/isestr.mjs'
    import isearr from 'wsemi/src/isearr.mjs'
    import isarr from 'wsemi/src/isarr.mjs'
    import isobj from 'wsemi/src/isobj.mjs'
    import isstr from 'wsemi/src/isstr.mjs'
    import isstr0 from 'wsemi/src/isstr0.mjs'
    import isundefined from 'wsemi/src/isundefined.mjs'
    import genPm from 'wsemi/src/genPm.mjs'

    /**
     * Promise化的JSON.parse
     *
     * @param {String} str 輸入字串
     * @return {Promise} 回傳Promise，resolve代表parse成功，回傳物件或陣列，reject代表失敗，回傳錯誤訊息
     */
    async function parse(str) {

        //check
        if (!isestr(str)) {
            return {}
        }

        let r = {}
        try {
            r = JSON.parse(str)
        }
        catch (err) {
            r = {}
        }

        return r
    }

    /**
     * 以stream為基底的JSON.parse
     *
     * @param {String} str 輸入字串
     * @return {Promise} 回傳Promise，resolve代表parse成功，回傳物件或陣列，reject代表失敗，回傳錯誤訊息
     */
    function parseByStream(str) {
        let accumulator = {}

        //check
        if (str === 'null') {
            return null
        }
        if (str === '') {
            return {}
        }

        //wrap, 因createParseStream內accumulator使用物件接收, 故外層一定為物件, 得自己包一層使parse能支援陣列
        let keyWrap = '__system__'
        let strWrap = `{"${keyWrap}":${str}}`

        //pm
        let pm = genPm()

        //sourceStream
        let sourceStream = intoStream(strWrap)

        //parseStream
        let parseStream = JSONStream.parse('$*')

        //onData
        parseStream.on('data', function(chunk) {
            // console.log('parseStream data chunk=', chunk)
            accumulator[chunk.key] = chunk.value
        })

        //onEnd
        parseStream.on('end', function() {
            // console.log('parseStream end accumulator=', accumulator)
            let res = get(accumulator, keyWrap)
            pm.resolve(res)
        })

        //onError
        parseStream.on('error', function(err) {
            pm.reject(err)
        })

        //pipe
        sourceStream.pipe(parseStream)

        return pm
    }

    /**
     * JSON反序列化的stream
     *
     * @param {String|Array} [filter='$*'] 輸入過濾字串或陣列，預設'$*'
     * @return {Stream} 回傳Stream，為Nodejs的stream物件
     */
    function createParseStream(filter) {
        //無法運行於web worker內, 因會被編譯成async function

        //check
        if (!isestr(filter) && !isearr(filter)) {
            filter = '$*'
        }

        return JSONStream.parse(filter)
    }

    /**
     * Promise化的JSON.stringify
     *
     * @param {Object|Array} data 輸入物件或陣列
     * @return {Promise} 回傳Promise，resolve代表parse成功，回傳序列化字串，reject代表失敗，回傳錯誤訊息
     */
    async function stringify(data) {

        //check
        if (isundefined(data)) {
            return ''
        }

        let r = ''
        try {
            r = JSON.stringify(data)
        }
        catch (err) {
            r = ''
        }

        return r
    }

    /**
     * 以stream為基底的JSON.stringify
     *
     * @param {Object|Array} data 輸入物件或陣列
     * @return {Promise} 回傳Promise，resolve代表parse成功，回傳序列化字串，reject代表失敗，回傳錯誤訊息
     */
    function stringifyByStream(data) {
        let stringified = ''

        //check
        if (isundefined(data)) {
            return ''
        }
        if (data === null) {
            return 'null'
        }
        if (isstr0(data)) {
            return '""'
        }
        if (isNumber(data)) {
            return `${data}`
        }
        if (isstr(data)) {
            return `"${data}"`
        }

        //pm
        let pm = genPm()

        //stringifyStream
        let stringifyStream = new JsonStreamStringify(data, null, null, false)

        //onData
        stringifyStream.on('data', function(chunk) {
            stringified += chunk
        })

        //onEnd
        stringifyStream.on('end', function() {
            pm.resolve(stringified)
        })

        //onError
        stringifyStream.on('error', function(err) {
            pm.reject(err)
        })

        return pm
    }

    /**
     * JSON序列化的stream
     *
     * @param {Object|Array} data 輸入物件或陣列
     * @return {Stream} 回傳Stream，為Nodejs的stream物件
     */
    function createStringifyStream(data) {
        //無法運行於web worker內, 因會被編譯成async function

        //check
        if (!isobj(data) && !isarr(data)) {
            throw new Error('data is not object or array')
        }

        return new JsonStreamStringify(data, null, null, false)
    }

    /**
     * 基於串流stream的JSON序列化(stringify)與反序列化(parse)
     *
     * @returns {Object} 回傳物件，其具有stringify、stringifyByStream、createParseStream、parse、parseByStream、createStringifyStream
     */
    let WJsonStream = {
        parse,
        parseByStream,
        createParseStream,
        stringify,
        stringifyByStream,
        createStringifyStream,
    }

    export default WJsonStream