1use crate::resolve::Resolved;
4use crate::schema::{self, Schema};
5
6#[derive(Debug, Default)]
7pub struct Report {
8 pub missing: Vec<String>,
10 pub invalid: Vec<(String, String)>,
12 pub extra: Vec<String>,
14}
15
16impl Report {
17 pub fn passed(&self) -> bool {
20 self.missing.is_empty() && self.invalid.is_empty()
21 }
22
23 pub fn failed(&self, strict: bool) -> bool {
24 !self.passed() || (strict && !self.extra.is_empty())
25 }
26}
27
28pub fn validate(schema: &Schema, resolved: &Resolved) -> Report {
29 let mut report = Report::default();
30
31 for spec in &schema.vars {
32 match resolved.values.get(&spec.name) {
33 None => {
34 if spec.required() {
35 report.missing.push(spec.name.clone());
36 }
37 }
38 Some(value) => {
39 if let Err(reason) = schema::validate_value(&spec.ty, value) {
40 report.invalid.push((spec.name.clone(), reason));
41 }
42 }
43 }
44 }
45
46 for key in resolved.values.keys() {
47 if schema.get(key).is_none() {
48 report.extra.push(key.to_string());
49 }
50 }
51
52 report
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::resolve::Resolved;
59
60 fn resolved_from(pairs: &[(&str, &str)]) -> Resolved {
61 let mut r = Resolved::default();
62 for (k, v) in pairs {
63 r.values.set(k.to_string(), v.to_string());
64 }
65 r
66 }
67
68 #[test]
69 fn flags_missing_and_invalid_and_extra() {
70 let schema = Schema::parse("PORT: port\nKEY: secret\nDEBUG: bool = false\n").unwrap();
71 let resolved = resolved_from(&[("PORT", "not-a-port"), ("EXTRA", "1")]);
72 let report = validate(&schema, &resolved);
73
74 assert!(report.missing.contains(&"KEY".to_string()));
75 assert!(report.invalid.iter().any(|(n, _)| n == "PORT"));
76 assert!(report.extra.contains(&"EXTRA".to_string()));
77 assert!(!report.passed());
78 assert!(report.failed(false));
79 }
80
81 #[test]
82 fn passes_when_satisfied() {
83 let schema = Schema::parse("PORT: port = 3000\nDEBUG: bool = false\n").unwrap();
84 let resolved = resolved_from(&[("PORT", "8080"), ("DEBUG", "true")]);
85 let report = validate(&schema, &resolved);
86 assert!(report.passed());
87 assert!(!report.failed(false));
88 }
89}