Compare commits

...
4 Commits
5 changed files with 139 additions and 2 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
tab_spaces = 2
+25
View File
@@ -0,0 +1,25 @@
use clap::Parser;
#[derive(Debug, Parser)]
#[command(name = "onx")]
#[command(version)]
#[command(about)]
pub struct Cli {
#[arg(long)]
#[arg(short = 'k')]
#[arg(env = "ONSPRING_API_KEY")]
#[arg(help = "The API key for the Onspring instance")]
pub api_key: Option<String>,
#[arg(long)]
#[arg(short = 'u')]
#[arg(env = "ONSPRING_BASE_URL")]
#[arg(help = "The base URL for the Onspring API")]
pub base_url: Option<String>,
#[arg(long)]
#[arg(short = 'p')]
#[arg(help = "Pretty-print JSON output")]
#[arg(default_value_t = false)]
pub pretty: bool,
}
+58
View File
@@ -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<String>) -> Self {
Self {
code: 0,
message: message.into(),
}
}
pub fn runtime(message: impl Into<String>) -> Self {
Self {
code: 1,
message: message.into(),
}
}
pub fn usage(message: impl Into<String>) -> Self {
Self {
code: 2,
message: message.into(),
}
}
}
pub type CliResult<T> = Result<T, CliError>;
pub async fn run(_cli: Cli) -> CliResult<()> {
Ok(())
}
pub fn parse_cli_from<I, T>(args: I) -> CliResult<Cli>
where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + 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())
}
+22 -2
View File
@@ -1,3 +1,23 @@
fn main() { use std::process;
println!("Hello, world!");
use onx::{parse_cli_from, render_cli_error, run};
#[tokio::main]
async fn main() {
let cli = match parse_cli_from(std::env::args_os()) {
Ok(cli) => cli,
Err(err) => {
if err.code == 0 {
print!("{}", err.message);
process::exit(0);
}
eprintln!("{}", render_cli_error(&err, false));
process::exit(err.code)
}
};
if let Err(err) = run(cli).await {
eprintln!("{}", render_cli_error(&err, false));
process::exit(err.code)
}
} }
+33
View File
@@ -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<T: Serialize>(value: &T, pretty: bool) -> CliResult<String> {
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<String> {
let envelope = ErrorEnvelope {
error: ErrorBody {
code: err.code,
message: &err.message,
},
};
render_json(&envelope, pretty)
}