UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

107 lines • 4.02 kB
import { specOf } from "../../spec/builtins.js"; import { FlagView } from "../../spec/flag_view.js"; import { IOResult } from "../../../io/types.js"; import { StreamRefusal } from "../../../rs/errors.js"; import { closeStream, openStream, streamOutputChunks } from "../../../rs/stream.js"; import { OutputStream, closeQuietly } from "../../../io/stream.js"; import { byteView } from "../../../shell/bytes.js"; import { concat } from "../../../utils/bytes.js"; const ENC = new TextEncoder(); // A refusal stops the conversion after the records before it, exit 2. async function* converted(stream, source, io, signal) { // The fields --invalid let through, said of and counted as each part // comes, while the stream is open: closing it releases them. const said = []; try { for await (const part of streamOutputChunks(stream, source, signal)) { said.push(stream.takeStderr()); io.exitCode = stream.status(); yield part; } } catch (err) { if (!(err instanceof StreamRefusal)) throw err; io.exitCode = 2; io.stderr = ENC.encode(`${err.message}\n`); if (err.output.byteLength > 0) yield err.output; return; } const warnings = concat(said); if (warnings.byteLength > 0) io.stderr = warnings; } // Every typed value of an option, a bare --header counting one record. function typed(fl, name) { return fl.occurrences(name).map(([, value]) => byteView(value === true ? '1' : String(value))); } export async function numfmtGeneric(texts, ctx) { const fl = new FlagView(ctx.flags, specOf('numfmt')); const to = fl.asStr('to') ?? 'none'; // mirage's one locale is C, whose thousands separator is empty, so // --grouping groups nothing; GNU still refuses it beside a --to scale. if (fl.asBool('grouping') && to !== 'none') { return [ null, new IOResult({ exitCode: 1, stderr: ENC.encode('numfmt: grouping cannot be combined with --to\n'), }), ]; } const layout = fl.asStr('format'); if (fl.asBool('grouping') && layout !== undefined) { return [ null, new IOResult({ exitCode: 1, stderr: ENC.encode('numfmt: --grouping cannot be combined with --format\n'), }), ]; } const typedText = (text) => text === undefined ? null : byteView(text); const config = { to, from: fl.asStr('from') ?? 'none', suffix: byteView(fl.asStr('suffix') ?? ''), operands: texts.map((text) => byteView(text)), fields: typed(fl, 'field'), delimiters: typed(fl, 'delimiter'), headers: typed(fl, 'header'), zero_terminated: fl.asBool('zero_terminated'), from_unit: typedText(fl.asStr('from_unit')), to_unit: typedText(fl.asStr('to_unit')), round: fl.asStr('round') ?? 'from-zero', padding: typedText(fl.asStr('padding')), format: typedText(layout), invalid: fl.asStr('invalid') ?? 'abort', }; let stream; try { stream = await openStream('numfmt', config); } catch (err) { if (!(err instanceof StreamRefusal)) throw err; return [null, new IOResult({ exitCode: 1, stderr: ENC.encode(`${err.message}\n`) })]; } const io = new IOResult(); const source = texts.length > 0 ? new Uint8Array(0) : (ctx.stdin ?? new Uint8Array(0)); // Closing the output releases the stream and, unread, the records. let started = false; async function* output() { started = true; yield* converted(stream, source, io, ctx.signal); } const producer = output(); return [ new OutputStream(producer, async () => { await closeStream(stream); if (!started) await closeQuietly(source); }), io, ]; } //# sourceMappingURL=numfmt.js.map