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(); }