import isbol from 'wsemi/src/isbol.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import isarr from 'wsemi/src/isarr.mjs'
import ispint from 'wsemi/src/ispint.mjs'
import isp0int from 'wsemi/src/isp0int.mjs'
import cint from 'wsemi/src/cint.mjs'
import cbol from 'wsemi/src/cbol.mjs'
//共用之取值與檢核: 型別不符一律採預設值, 不拋錯
//本套件之公開函數皆以「不reject、非法輸入退回預設」為契約, 故各選項一律經此處理
//
//── 各型別之寬容度並不一致, 此為刻意 ──
// 數字類(getOptPInt/getOptP0Int) **接受數字字串並轉為數字**: 逾時、埠號、重試次數
// 常來自環境變數、CLI參數或JSON設定, 到手即為字串,
// 要求呼叫端自行轉型只會讓每個呼叫端各寫一次parseInt
// 布林類(getOptBool) **只接受真布林**: 字串'false'為truthy, 一旦接受字串,
// 呼叫端寫'false'卻得到true, 是與其書寫相反的行為
// 字串類與陣列類 只接受該型別本身
//
//此差異由 unit-getOpt 之「各型別寬容度」一節鎖住。
//先前本註解寫的是「不嘗試強制轉換」, 與數字類之實作相反
function _pick(opt, key, def, isOk, cast) {
//單層鍵取值, 不需外部套件; opt非物件或鍵不存在皆得undefined而落入預設
let v = opt?.[key]
if (!isOk(v)) {
return def
}
return cast ? cast(v) : v
}
//數字類之檢核分兩層, 職責不同
//
//第一層 useLimitSafe(wsemi 1.8.90起): 回答「是不是安全整數」。
// 不加此選項時ispint(Infinity)為true——cdbl以lodash之toFinite把Infinity夾成
// Number.MAX_VALUE, 而該值是整數故判定通過。此為型別層的事, 由wsemi負責, 本套件不另寫
//
//第二層 MAX_TIMER_MS: 回答「這個值能不能進計時器」, 屬本套件對選項的契約, wsemi無從得知。
// 安全整數上限為2^53-1, 但setTimeout與child_process之timeout只吃32位元有號整數,
// 超過即溢位而被Node設為1毫秒。實測 timeout:3e9(是安全整數)使curl於22毫秒內被SIGTERM殺掉,
// timeoutMs:Infinity亦同——呼叫端寫的是「不要逾時」, 得到的卻是「立即逾時」。
// 逾時、等待間隔類選項皆會進計時器; 埠號與重試次數雖不會, 但其正當值遠小於此上限,
// 故統一套用不會擋掉任何合理用法
let OPT_SAFE = { useLimitSafe: true }
let MAX_TIMER_MS = 2147483647
function _inTimerRange(v) {
return cint(v) <= MAX_TIMER_MS
}
/**
* 由設定物件取布林選項,型別不符時採預設值
*
* @param {Object} opt 輸入設定物件
* @param {String} key 輸入鍵名字串
* @param {Boolean} def 輸入預設值布林值
* @returns {Boolean} 回傳選項布林值
* @example
*
* import { getOptBool } from './src/getOpt.mjs'
*
* console.log(getOptBool({ parse: false }, 'parse', true), getOptBool({ parse: 'x' }, 'parse', true))
* // => false true
*
*/
function getOptBool(opt, key, def) {
return _pick(opt, key, def, isbol, cbol)
}
/**
* 由設定物件取正整數選項,型別不符時採預設值
*
* @param {Object} opt 輸入設定物件
* @param {String} key 輸入鍵名字串
* @param {Integer} def 輸入預設值整數
* @returns {Integer} 回傳選項整數
* @example
*
* import { getOptPInt } from './src/getOpt.mjs'
*
* console.log(getOptPInt({ timeoutMs: 3000 }, 'timeoutMs', 15000), getOptPInt({ timeoutMs: 0 }, 'timeoutMs', 15000))
* // => 3000 15000
*
*/
function getOptPInt(opt, key, def) {
return _pick(opt, key, def, (v) => ispint(v, OPT_SAFE) && _inTimerRange(v), cint)
}
/**
* 由設定物件取非負整數選項,型別不符時採預設值
*
* @param {Object} opt 輸入設定物件
* @param {String} key 輸入鍵名字串
* @param {Integer} def 輸入預設值整數
* @returns {Integer} 回傳選項整數
* @example
*
* import { getOptP0Int } from './src/getOpt.mjs'
*
* console.log(getOptP0Int({ maxRetries: 0 }, 'maxRetries', 5), getOptP0Int({ maxRetries: -1 }, 'maxRetries', 5))
* // => 0 5
*
*/
function getOptP0Int(opt, key, def) {
return _pick(opt, key, def, (v) => isp0int(v, OPT_SAFE) && _inTimerRange(v), cint)
}
/**
* 由設定物件取非空字串選項,型別不符時採預設值
*
* @param {Object} opt 輸入設定物件
* @param {String} key 輸入鍵名字串
* @param {String} def 輸入預設值字串
* @returns {String} 回傳選項字串
* @example
*
* import { getOptStr } from './src/getOpt.mjs'
*
* console.log(getOptStr({ method: 'curl' }, 'method', 'auto'), getOptStr({ method: '' }, 'method', 'auto'))
* // => 'curl' 'auto'
*
*/
function getOptStr(opt, key, def) {
return _pick(opt, key, def, isestr, null)
}
/**
* 由設定物件取陣列選項,型別不符時採預設值
*
* @param {Object} opt 輸入設定物件
* @param {String} key 輸入鍵名字串
* @param {Array} def 輸入預設值陣列
* @returns {Array} 回傳選項陣列
* @example
*
* import { getOptArr } from './src/getOpt.mjs'
*
* console.log(getOptArr({ adapters: [1] }, 'adapters', []), getOptArr({ adapters: 'x' }, 'adapters', []))
* // => [1] []
*
*/
function getOptArr(opt, key, def) {
return _pick(opt, key, def, isarr, null)
}
export {
getOptBool,
getOptPInt,
getOptP0Int,
getOptStr,
getOptArr
}