UNPKG

@davidcal/fec-raptorq

Version:

Node.js wrapper for RaptorQ forward error correction

256 lines (215 loc) 6.16 kB
import { is_enum } from "./is_enum.js"; import { is_map } from "./is_map.js"; import { map } from "./map.js"; import { obtain_map } from "./obtain_map.js"; import { create_promise } from "./create_promise.js"; import { api } from "./api.js"; /** * @stability 1 - experimental * * Takes in an unpacker function and produces a map that destructures the various scenarios that the input might take on (whether the input be a uoe-map, a uoe-enum, or undefined). * * Recall that "undefined" is the leaf case, specifying the desire to obtain the final return value of the map. * * Otherwise, the input may be an enum or a map. Be careful not to inadvertently pass in a map that resolves to an enum leaf, when it is desired that the enum be passed in instead. Take care to await this if necessary. * * TODO: should notion of uoe-api be incorporated directly into this notation? * * @example * * const my_map = unpacker_map(($) => { * $.symbol_name(($) => { * // ... * }); * * $.symbol_name.$ret_lazy(() => "example"); * $.symbol_name.$ret("example"); // shorthand * $.symbol_name.$call((map) => {}); * $.symbol_name.$fall((inp) => {}); * $.$ret(() => "example") * $.$("example"); // shorthand * $.$call((map) => {}); * $.$fall((inp) => {}); * }); * * const person = unpacker_map(($) => { * $.name.$ret("John Doe"); * $.age.$ret(27); * $.friends.$ret(["mary", "jane", "joseph"]); * $.address(($) => { * $.street.$ret("123 Main St"); * $.city.$ret("Monopoly Town"); * $.state.$ret("CA"); * }); * }); */ export const unpacker_map = (unpacker) => obtain_map((async () =>{ let self_ret = undefined; let self_call = undefined; let self_fall = undefined; const symbols = new Map(); const builder = new Proxy({}, { get: (target, prop) => { if (prop === "$call") { return (callback) => { self_call = callback; }; } if (prop === "$fall") { return (fallback) => { self_fall = fallback; }; } if (prop === "$ret") { return (value) => { self_ret = () => value; }; } if (prop === "$ret_lazy") { return (lazy_value) => { self_ret = lazy_value; }; } if (prop === "$ret_api") { return (unpacker) => { self_ret = () => { return api(async () => { const [explicit_prom, res_explicit, _rej] = create_promise(); const implicit = unpacker_map(async ($) => { res_explicit(await unpacker($)); }); const explicit = await explicit_prom; if (explicit !== undefined) { return explicit; } return implicit; }); }; }; } if (prop === "$call_api") { return (callback) => { self_call = (input) => { return api(async () => { const [explicit_prom, res_explicit, _rej] = create_promise(); const implicit = unpacker_map(async ($) => { res_explicit(await callback(input, $)); }); const explicit = await explicit_prom; if (explicit !== undefined) { return explicit; } return implicit; }); }; }; } if (/^[a-zA-Z0-9_]+$/.test(prop)) { return new Proxy((unpacker) => { symbols.set(prop, symbols.get(prop) ?? {}); symbols.get(prop).fall = unpacker_map(unpacker); }, { get: (target, nested_prop) => { if (nested_prop === "$call") { return (callback) => { symbols.set(prop, symbols.get(prop) ?? {}); symbols.get(prop).call = callback; }; } if (nested_prop === "$call_api") { return (callback) => { symbols.set(prop, symbols.get(prop) ?? {}); symbols.get(prop).call_api = callback; }; } if (nested_prop === "$fall") { return (fallback) => { symbols.set(prop, symbols.get(prop) ?? {}); symbols.get(prop).fall = fallback; }; } if (nested_prop === "$ret") { return (value) => { symbols.set(prop, symbols.get(prop) ?? {}); symbols.get(prop).ret = () => value; }; } if (nested_prop === "$ret_lazy") { return (lazy_value) => { symbols.set(prop, symbols.get(prop) ?? {}); symbols.get(prop).ret = lazy_value; }; } if (nested_prop === "$ret_api") { return (unpacker) => { symbols.set(prop, symbols.get(prop) ?? {}); symbols.get(prop).ret_api = unpacker; }; } return target[nested_prop]; }, }); } return target[prop]; }, }); await unpacker(builder); const symbol_maps = new Map(); for (const [sym, entry] of symbols) { symbol_maps.set(sym, unpacker_map(($) => { if (entry.ret !== undefined) { $.$ret_lazy(entry.ret); } if (entry.ret_api !== undefined) { $.$ret_api(entry.ret_api); } if (entry.call !== undefined) { $.$call(entry.call); } if (entry.call_api !== undefined) { $.$call_api(entry.call_api); } if (entry.fall !== undefined) { $.$fall(entry.fall); } })); } return map(async (input) => { if (true && !is_map(input) && !is_enum(input) && input !== undefined ) { throw user_payload_error("Expected uoe-map, uoe-enum or `undefined` as input to map. Consider using `obtain_map` or awaiting an enum leaf if appropriate."); } if (true && self_ret !== undefined && input === undefined ) { return self_ret(); } if (true && self_call !== undefined && is_map(input) ) { const [explicit_prom, res_explicit, _rej] = create_promise(); const implicit = unpacker_map(async ($) => { res_explicit(await self_call(input, $)); }); const explicit = await explicit_prom; if (explicit !== undefined) { return explicit; } return implicit; } if (true && is_enum(input) && symbol_maps.get(input.sym) !== undefined ) { return symbol_maps.get(input.sym); } if (self_fall !== undefined) { return self_fall(input); } }); })());