Refactored CLI to support multiple commands as sk-az-tools do. Added table-info command.
All checks were successful
build / build (push) Successful in 10s
All checks were successful
build / build (push) Successful in 10s
This commit is contained in:
126
src/cli/commands/table-info.ts
Normal file
126
src/cli/commands/table-info.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import {
|
||||
readCsvFromStdin,
|
||||
readJsonFromStdin,
|
||||
} from "../utils.ts";
|
||||
|
||||
type TableInfoCommandValues = {
|
||||
from?: string;
|
||||
[key: string]: string | boolean | undefined;
|
||||
};
|
||||
|
||||
type RowObject = Record<string, unknown>;
|
||||
|
||||
type ValueType = "string" | "number" | "boolean" | "object" | "array" | "null" | "unknown";
|
||||
|
||||
function getType(value: unknown): ValueType {
|
||||
if (value === null) return "null";
|
||||
if (Array.isArray(value)) return "array";
|
||||
if (value === undefined) return "unknown";
|
||||
if (typeof value === "string") return "string";
|
||||
if (typeof value === "number") return "number";
|
||||
if (typeof value === "boolean") return "boolean";
|
||||
if (typeof value === "object") return "object";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function normalizeRows(input: unknown): { rowCount: number; rows: RowObject[] } {
|
||||
if (Array.isArray(input)) {
|
||||
return {
|
||||
rowCount: input.length,
|
||||
rows: input.map((item) =>
|
||||
item && typeof item === "object" && !Array.isArray(item)
|
||||
? item as RowObject
|
||||
: { value: item }
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (input && typeof input === "object") {
|
||||
return {
|
||||
rowCount: 1,
|
||||
rows: [input as RowObject],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
rowCount: 1,
|
||||
rows: [{ value: input }],
|
||||
};
|
||||
}
|
||||
|
||||
function inferColumnType(rows: RowObject[], column: string): string {
|
||||
const kinds = new Set<ValueType>();
|
||||
|
||||
for (const row of rows) {
|
||||
if (!(column in row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const kind = getType(row[column]);
|
||||
if (kind !== "unknown") {
|
||||
kinds.add(kind);
|
||||
}
|
||||
}
|
||||
|
||||
if (kinds.size === 0) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
if (kinds.size === 1) {
|
||||
return [...kinds][0];
|
||||
}
|
||||
|
||||
if (kinds.size === 2 && kinds.has("null")) {
|
||||
return [...kinds].find((kind) => kind !== "null") ?? "unknown";
|
||||
}
|
||||
|
||||
return `mixed(${[...kinds].join("|")})`;
|
||||
}
|
||||
|
||||
function buildInfo(input: unknown): string {
|
||||
const { rowCount, rows } = normalizeRows(input);
|
||||
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
||||
|
||||
const lines = [
|
||||
`Number of columns: ${columns.length}`,
|
||||
`Number of rows: ${rowCount}`,
|
||||
"",
|
||||
"Columns:",
|
||||
];
|
||||
|
||||
if (columns.length === 0) {
|
||||
lines.push("- (none)");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
for (const column of columns) {
|
||||
lines.push(`- ${column}: ${inferColumnType(rows, column)}`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
export function usageTableInfo(): string {
|
||||
return `Usage: sk-tools table-info [--from|-F <json|csv|tsv>]
|
||||
|
||||
Options:
|
||||
--from, -F <json|csv|tsv> Input format on stdin (default: json)`;
|
||||
}
|
||||
|
||||
export async function runTableInfoCommand(values: TableInfoCommandValues): Promise<string> {
|
||||
const from = (values.from ?? "json").toString().trim().toLowerCase();
|
||||
|
||||
if (from === "json") {
|
||||
return buildInfo(await readJsonFromStdin());
|
||||
}
|
||||
if (from === "csv") {
|
||||
return buildInfo(await readCsvFromStdin(","));
|
||||
}
|
||||
if (from === "tsv") {
|
||||
return buildInfo(await readCsvFromStdin("\t"));
|
||||
}
|
||||
|
||||
throw new Error(`Invalid --from '${values.from}'. Allowed: json, csv, tsv`);
|
||||
}
|
||||
35
src/cli/commands/table.ts
Normal file
35
src/cli/commands/table.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import {
|
||||
readCsvFromStdin,
|
||||
readJsonFromStdin,
|
||||
} from "../utils.ts";
|
||||
|
||||
type TableCommandValues = {
|
||||
from?: string;
|
||||
[key: string]: string | boolean | undefined;
|
||||
};
|
||||
|
||||
export function usageTable(): string {
|
||||
return `Usage: sk-tools table [--from|-F <json|csv|tsv>] [--columns|-C <columns>] [global options]
|
||||
|
||||
Options:
|
||||
--from, -F <json|csv|tsv> Input format on stdin (default: json)
|
||||
--columns, -C <value> Column tokens: col (raw), col: (auto), col:Label (custom), with exact match via = prefix (e.g. =col:)`;
|
||||
}
|
||||
|
||||
export async function runTableCommand(values: TableCommandValues): Promise<unknown> {
|
||||
const from = (values.from ?? "json").toString().trim().toLowerCase();
|
||||
|
||||
if (from === "json") {
|
||||
return readJsonFromStdin();
|
||||
}
|
||||
if (from === "csv") {
|
||||
return readCsvFromStdin(",");
|
||||
}
|
||||
if (from === "tsv") {
|
||||
return readCsvFromStdin("\t");
|
||||
}
|
||||
|
||||
throw new Error(`Invalid --from '${values.from}'. Allowed: json, csv, tsv`);
|
||||
}
|
||||
Reference in New Issue
Block a user