config_check.rs in Rust
A validation pipeline: parse key-value text, collect every problem, report.
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
#[derive(Debug)]
enum Invalid {
Missing(&'static str),
NotANumber { key: &'static str, got: String },
OutOfRange { key: &'static str, got: i64 },
}
impl fmt::Display for Invalid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Invalid::Missing(key) => write!(f, "{key} is required"),
Invalid::NotANumber { key, got } => {
write!(f, "{key} should be a number, got {got:?}")
}
Invalid::OutOfRange { key, got } => {
write!(f, "{key} is out of range: {got}")
}
}
}
}
fn parse(raw: &str) -> HashMap<&str, &str> {
raw.lines()
.filter_map(|line| line.trim().split_once('='))
.map(|(k, v)| (k.trim(), v.trim()))
.collect()
}
fn number(
fields: &HashMap<&str, &str>,
key: &'static str,
range: std::ops::RangeInclusive<i64>,
) -> Result<i64, Invalid> {
let raw = fields.get(key).ok_or(Invalid::Missing(key))?;
let value: i64 = raw.parse().map_err(|_| Invalid::NotANumber {
key,
got: raw.to_string(),
})?;
if !range.contains(&value) {
return Err(Invalid::OutOfRange { key, got: value });
}
Ok(value)
}
fn check(raw: &str) -> Vec<Invalid> {
let fields = parse(raw);
let mut problems = Vec::new();
if !fields.contains_key("host") {
problems.push(Invalid::Missing("host"));
}
for (key, range) in [("port", 1..=65535), ("workers", 1..=64)] {
if let Err(e) = number(&fields, key, range) {
problems.push(e);
}
}
problems
}
fn main() -> Result<(), Box<dyn Error>> {
let raw = "host = typestar.io\nport = 99999\nworkers = many";
let problems = check(raw);
if problems.is_empty() {
println!("config looks good");
return Ok(());
}
println!("{} problem(s):", problems.len());
for p in &problems {
println!(" - {p}");
}
Ok(())
}
How it works
- One error enum covers the three ways a field can be wrong.
- Validation gathers all failures instead of stopping at the first.
mainreturnsResult, so a fatal parse error exits non-zero.
Keywords and builtins used here
BoxErrHashMapInvalidOkResultStringVeccheckdynenumfmtfnfori64ifimplinletmainmatchmutnumberparserawreturnselfstaticstdstrusevalue
The run, in numbers
- Lines
- 78
- Characters to type
- 1704
- Tokens
- 554
- Three-star pace
- 110 tpm
At the three-star pace of 110 tokens a minute, this run takes about 302 seconds.
Step 1 of 1 in Encore, step 15 of 15 in Error handling.