summaryrefslogtreecommitdiffstats
path: root/src/bin
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-26 12:17:46 -0300
committerMurilo Ijanc <[email protected]>2026-07-26 12:17:46 -0300
commit4a8c3b675df4b1a6b29904fc2af60605002a7f9e (patch)
tree5c3dcc6594603d242b6a284c1601fd1ffbfcc8f4 /src/bin
downloadrust-f04e6cf2da99b276c9988f1f98f7ca4059191365.tar.gz
Initial importv0.1.0
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/cepis.rs100
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
+}