1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
}
|