use reliakit::primitives::{BoundedStr, Email, Port};
use reliakit::secret::{ExposeSecret, SecretString};
use reliakit::validate::{ValidationError, Violation};
type ServiceName = BoundedStr<3, 32>;
struct RawConfig {
name: &'static str,
port: &'static str,
admin_email: &'static str,
api_key: &'static str,
}
struct ServiceConfig {
name: ServiceName,
port: Port,
admin_email: Email,
api_key: SecretString,
}
impl ServiceConfig {
fn parse(raw: &RawConfig) -> Result<ServiceConfig, ValidationError> {
let mut errors = ValidationError::empty();
let name = ServiceName::new(raw.name).ok();
if name.is_none() {
errors.push(Violation::with_field("name", "must be 3-32 characters"));
}
let port = raw.port.parse::<u16>().ok().and_then(|p| Port::new(p).ok());
if port.is_none() {
errors.push(Violation::with_field("port", "must be a number in 1-65535"));
}
let admin_email = Email::new(raw.admin_email).ok();
if admin_email.is_none() {
errors.push(Violation::with_field(
"admin_email",
"must be a valid email",
));
}
let api_key = SecretString::from_string(raw.api_key);
if api_key.expose_secret().len() < 8 {
errors.push(Violation::with_field(
"api_key",
"must be at least 8 characters",
));
}
match (name, port, admin_email) {
(Some(name), Some(port), Some(admin_email)) if errors.is_empty() => Ok(ServiceConfig {
name,
port,
admin_email,
api_key,
}),
_ => Err(errors),
}
}
}
fn main() {
let bad = RawConfig {
name: "x",
port: "99999",
admin_email: "not-an-email",
api_key: "short",
};
println!("checking a bad config:");
match ServiceConfig::parse(&bad) {
Ok(_) => println!(" ok"),
Err(errors) => {
for v in errors.violations() {
println!(" - {}: {}", v.field.unwrap_or("(config)"), v.message);
}
}
}
let good = RawConfig {
name: "api-service",
port: "8080",
admin_email: "admin@example.com",
api_key: "rk_live_secret_value",
};
println!("\nchecking a good config:");
match ServiceConfig::parse(&good) {
Ok(config) => {
println!(" service '{}' on port {}", config.name, config.port);
println!(" admin: {}", config.admin_email);
println!(" api key (display): {}", config.api_key); println!(" api key length: {}", config.api_key.expose_secret().len());
}
Err(_) => println!(" unexpected validation error"),
}
}