diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 12:17:46 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 12:17:46 -0300 |
| commit | 4a8c3b675df4b1a6b29904fc2af60605002a7f9e (patch) | |
| tree | 5c3dcc6594603d242b6a284c1601fd1ffbfcc8f4 /src/bin/cepis.rs | |
| download | rust-4a8c3b675df4b1a6b29904fc2af60605002a7f9e.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'src/bin/cepis.rs')
| -rw-r--r-- | src/bin/cepis.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/bin/cepis.rs b/src/bin/cepis.rs new file mode 100644 index 0000000..252cb90 --- /dev/null +++ b/src/bin/cepis.rs @@ -0,0 +1,100 @@ +//! Minimal CLI: `cepis 01001000`. +//! +//! It exists for one concrete case — quickly confirming a local instance +//! answers before pointing code at it: +//! +//! ```sh +//! cepis --base-url http://localhost:8080 01001000 +//! ``` +//! +//! A single CEP goes through the unit lookup, which carries `geo`; several go +//! through the batch, which is one request instead of N and has no `geo`. +//! +//! It is not the library's main interface; for that, use the `cepis` crate. + +use std::process::ExitCode; +use std::time::Duration; + +use cepis::{Client, DEFAULT_URL}; +use serde_json::Value; + +const USAGE: &str = "\ +usage: cepis [--base-url URL] [--timeout SECONDS] CEP [CEP ...] + +Look CEPs up against the cep.is API. + + --base-url URL API URL (default: https://cep.is) — point it at a local instance + --timeout SECONDS per-socket timeout (default: 10) + -h, --help show this message"; + +fn main() -> ExitCode { + let mut base_url = DEFAULT_URL.to_owned(); + let mut timeout = Duration::from_secs(10); + let mut ceps: Vec<String> = Vec::new(); + let mut args = std::env::args().skip(1); + + while let Some(arg) = args.next() { + match arg.as_str() { + "-h" | "--help" => { + println!("{USAGE}"); + return ExitCode::SUCCESS; + } + "--base-url" => match args.next() { + Some(value) => base_url = value, + None => return fail("--base-url needs a URL"), + }, + "--timeout" => match args.next().map(|v| v.parse::<f64>()) { + Some(Ok(seconds)) if seconds > 0.0 => timeout = Duration::from_secs_f64(seconds), + _ => return fail("--timeout needs a positive number of seconds"), + }, + other if other.starts_with('-') => return fail(&format!("unknown option {other}")), + other => ceps.push(other.to_owned()), + } + } + + if ceps.is_empty() { + eprintln!("{USAGE}"); + return ExitCode::FAILURE; + } + + let client = Client::builder() + .base_url(base_url) + .timeout(timeout) + .build(); + + let addresses = if ceps.len() == 1 { + match client.lookup(&ceps[0]) { + Ok(address) => vec![Some(address)], + Err(err) => return fail(&err.to_string()), + } + } else { + match client.lookup_many(&ceps) { + Ok(found) => found, + Err(err) => return fail(&err.to_string()), + } + }; + + let output: Vec<Value> = addresses + .into_iter() + .map(|a| a.map(|a| a.raw).unwrap_or(Value::Null)) + .collect(); + + let rendered = if output.len() == 1 { + serde_json::to_string_pretty(&output[0]) + } else { + serde_json::to_string_pretty(&output) + }; + + match rendered { + Ok(text) => { + println!("{text}"); + ExitCode::SUCCESS + } + Err(err) => fail(&err.to_string()), + } +} + +fn fail(message: &str) -> ExitCode { + eprintln!("error: {message}"); + ExitCode::FAILURE +} |