@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
107 lines • 4.02 kB
JavaScript
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