winbrew_models\shared/
validation.rs1use super::error::ModelError;
8
9pub trait Validate {
11 fn validate(&self) -> Result<(), ModelError>;
12}
13
14pub fn ensure_non_empty(field: &'static str, value: &str) -> Result<(), ModelError> {
16 if value.trim().is_empty() {
17 Err(ModelError::empty(field))
18 } else {
19 Ok(())
20 }
21}
22
23pub fn ensure_http_url(field: &'static str, value: &str) -> Result<(), ModelError> {
25 let parsed = url::Url::parse(value)
26 .map_err(|err| ModelError::invalid_url(field, format!("{value} ({err})")))?;
27
28 match parsed.scheme() {
29 "http" | "https" => Ok(()),
30 other => Err(ModelError::invalid_url(
31 field,
32 format!("{value} (unsupported scheme {other})"),
33 )),
34 }
35}
36
37pub fn ensure_hash(field: &'static str, value: &str) -> Result<(), ModelError> {
39 let normalized = value.trim();
40 if normalized.is_empty() {
41 return Err(ModelError::invalid_hash(field, value));
42 }
43
44 let candidate = normalized
45 .strip_prefix("sha256:")
46 .or_else(|| normalized.strip_prefix("sha1:"))
47 .or_else(|| normalized.strip_prefix("md5:"))
48 .or_else(|| normalized.strip_prefix("sha512:"))
49 .unwrap_or(normalized);
50
51 if candidate.chars().all(|ch| ch.is_ascii_hexdigit()) {
52 Ok(())
53 } else {
54 Err(ModelError::invalid_hash(field, value))
55 }
56}