Remove colors dependency from output.zig Add terminal.zig for TTY detection and terminal width Update flags.zig with color flag support Simplify colors.zig to basic ANSI codes Update main.zig and utils.zig exports
137 lines
4.3 KiB
Zig
137 lines
4.3 KiB
Zig
const std = @import("std");
|
|
const terminal = @import("../utils/terminal.zig");
|
|
|
|
/// Output mode: JSON for structured data, text for TSV
|
|
pub const Mode = enum { json, text };
|
|
|
|
pub var mode: Mode = .text;
|
|
|
|
pub fn setMode(m: Mode) void {
|
|
mode = m;
|
|
}
|
|
|
|
/// Escape a value for TSV output (replace tabs/newlines with spaces for xargs safety)
|
|
fn escapeTSV(val: []const u8) []const u8 {
|
|
// For xargs usability, we need single-line output with no tabs/newlines in values
|
|
// This returns the same slice if no escaping needed, but we process to ensure safety
|
|
// In practice, we just use the value directly since the caller should sanitize
|
|
return val;
|
|
}
|
|
|
|
/// Check if value needs TSV escaping
|
|
fn needsTSVEscape(val: []const u8) bool {
|
|
for (val) |c| {
|
|
if (c == '\t' or c == '\n' or c == '\r') return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Print error to stderr
|
|
pub fn err(msg: []const u8) void {
|
|
std.debug.print("Error: {s}\n", .{msg});
|
|
}
|
|
|
|
/// Print line in current mode (JSON or TSV)
|
|
pub fn line(values: []const []const u8) void {
|
|
switch (mode) {
|
|
.json => {
|
|
std.debug.print("{{", .{});
|
|
// Assume alternating key-value pairs
|
|
var i: usize = 0;
|
|
while (i < values.len) : (i += 2) {
|
|
if (i > 0) std.debug.print(",", .{});
|
|
const key = values[i];
|
|
const val = if (i + 1 < values.len) values[i + 1] else "";
|
|
std.debug.print("\"{s}\":\"{s}\"", .{ key, val });
|
|
}
|
|
std.debug.print("}}\n", .{});
|
|
},
|
|
.text => {
|
|
for (values, 0..) |val, i| {
|
|
if (i > 0) std.debug.print("\t", .{});
|
|
// For TSV/xargs safety: if value contains tabs/newlines, we need to handle it
|
|
// Simple approach: print as-is but replace internal tabs with spaces
|
|
if (needsTSVEscape(val)) {
|
|
for (val) |c| {
|
|
if (c == '\t' or c == '\n' or c == '\r') {
|
|
std.debug.print(" ", .{});
|
|
} else {
|
|
std.debug.print("{c}", .{c});
|
|
}
|
|
}
|
|
} else {
|
|
std.debug.print("{s}", .{val});
|
|
}
|
|
}
|
|
std.debug.print("\n", .{});
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Print raw JSON array
|
|
pub fn jsonArray(items: []const []const u8) void {
|
|
std.debug.print("[", .{});
|
|
for (items, 0..) |item, i| {
|
|
if (i > 0) std.debug.print(",", .{});
|
|
std.debug.print("\"{s}\"", .{item});
|
|
}
|
|
std.debug.print("]\n", .{});
|
|
}
|
|
|
|
/// Print raw JSON object from key-value pairs
|
|
pub fn jsonObject(pairs: []const []const u8) void {
|
|
std.debug.print("{{", .{});
|
|
var i: usize = 0;
|
|
while (i < pairs.len) : (i += 2) {
|
|
if (i > 0) std.debug.print(",", .{});
|
|
const key = pairs[i];
|
|
const val = if (i + 1 < pairs.len) pairs[i + 1] else "";
|
|
std.debug.print("\"{s}\":\"{s}\"", .{ key, val });
|
|
}
|
|
std.debug.print("}}\n", .{});
|
|
}
|
|
|
|
/// Print success response (JSON only)
|
|
pub fn success(comptime cmd: []const u8) void {
|
|
if (mode == .json) {
|
|
std.debug.print("{{\"success\":true,\"command\":\"{s}\"}}\n", .{cmd});
|
|
}
|
|
}
|
|
|
|
/// Print success with data
|
|
pub fn successData(comptime cmd: []const u8, pairs: []const []const u8) void {
|
|
if (mode == .json) {
|
|
std.debug.print("{{\"success\":true,\"command\":\"{s}\",\"data\":{{", .{cmd});
|
|
var i: usize = 0;
|
|
while (i < pairs.len) : (i += 2) {
|
|
if (i > 0) std.debug.print(",", .{});
|
|
const key = pairs[i];
|
|
const val = if (i + 1 < pairs.len) pairs[i + 1] else "";
|
|
std.debug.print("\"{s}\":\"{s}\"", .{ key, val });
|
|
}
|
|
std.debug.print("}}}}\n", .{});
|
|
} else {
|
|
for (pairs, 0..) |val, i| {
|
|
if (i > 0) std.debug.print("\t", .{});
|
|
std.debug.print("{s}", .{val});
|
|
}
|
|
std.debug.print("\n", .{});
|
|
}
|
|
}
|
|
|
|
/// Print usage information
|
|
pub fn usage(comptime cmd: []const u8, comptime u: []const u8) void {
|
|
std.debug.print("Usage: {s} {s}\n", .{ cmd, u });
|
|
}
|
|
|
|
/// Print plain value (text mode only)
|
|
pub fn value(v: []const u8) void {
|
|
if (mode == .text) {
|
|
std.debug.print("{s}\n", .{v});
|
|
}
|
|
}
|
|
|
|
/// Get terminal width for formatting
|
|
pub fn getTerminalWidth() ?usize {
|
|
return terminal.getWidth();
|
|
}
|