From 03c1c3329fccb20b186d3a121865f81e769197a5 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:59:38 -0500 Subject: [PATCH] feat: add guts for handling outputs and rendering them --- src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/output.rs | 33 +++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/lib.rs create mode 100644 src/output.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d2e8b59 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,58 @@ +mod cli; +mod output; + +use clap::{Parser, error::ErrorKind}; +use cli::Cli; + +use output::render_error; + +#[derive(Debug, PartialEq)] +pub struct CliError { + pub code: i32, + pub message: String, +} + +impl CliError { + pub fn info(message: impl Into) -> Self { + Self { + code: 0, + message: message.into(), + } + } + + pub fn runtime(message: impl Into) -> Self { + Self { + code: 1, + message: message.into(), + } + } + + pub fn usage(message: impl Into) -> Self { + Self { + code: 2, + message: message.into(), + } + } +} + +pub type CliResult = Result; + +pub async fn run(_cli: Cli) -> CliResult<()> { + Ok(()) +} + +pub fn parse_cli_from(args: I) -> CliResult +where + I: IntoIterator, + T: Into + Clone, +{ + Cli::try_parse_from(args).map_err(|e| match e.kind() { + ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => CliError::info(e.to_string()), + _ => CliError::usage(e.to_string()), + }) +} + +pub fn render_cli_error(err: &CliError, pretty: bool) -> String { + render_error(err, pretty) + .unwrap_or_else(|_| "{\"error\":{\"code\":1,\"message\":\"Unexpected error.\"}}".to_string()) +} diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..67497ae --- /dev/null +++ b/src/output.rs @@ -0,0 +1,33 @@ +use serde::Serialize; + +use crate::{CliError, CliResult}; + +#[derive(Debug, Serialize)] +struct ErrorBody<'a> { + pub code: i32, + pub message: &'a str, +} + +#[derive(Debug, Serialize)] +struct ErrorEnvelope<'a> { + pub error: ErrorBody<'a>, +} + +pub fn render_json(value: &T, pretty: bool) -> CliResult { + if pretty { + serde_json::to_string_pretty(value).map_err(|_| CliError::runtime("Failed to serialize output")) + } else { + serde_json::to_string(value).map_err(|_| CliError::runtime("Failed to serialize output")) + } +} + +pub fn render_error(err: &CliError, pretty: bool) -> CliResult { + let envelope = ErrorEnvelope { + error: ErrorBody { + code: err.code, + message: &err.message, + }, + }; + + render_json(&envelope, pretty) +}