Skip to content

Commit c6b65c3

Browse files
committed
quick n dirty YAML validator
1 parent d44c94b commit c6b65c3

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

chisel/src/config.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use std::error::Error;
2+
3+
use crate::fail;
4+
use crate::options::ChiselFlags;
5+
use serde_yaml::Value;
6+
7+
pub fn parse_config(flags: &ChiselFlags, buf: &String) {
8+
let yaml = serde_yaml::from_str::<Value>(buf).unwrap_or_else(|e| {
9+
fail(
10+
1,
11+
&format!("Failed to parse YAML configuration: {}", e.description()),
12+
)
13+
});
14+
}
15+
16+
pub fn validate_config(flags: &ChiselFlags, yaml: &Value) -> Result<(), String> {
17+
if flags.debug_enabled() {
18+
println!("Validating YAML configuration...");
19+
}
20+
21+
// rules
22+
// - top-level value must always be a mapping of string to mapping
23+
// - each child mapping must have a string field: file
24+
// - each child mapping must contain at least one string to mapping map (represents a module)
25+
match yaml {
26+
Value::Mapping(m) => {
27+
// Iterate over rulesets
28+
for (idx, (key, value)) in m.iter().enumerate() {
29+
// Rule 1
30+
match (key, value) {
31+
(Value::String(name), Value::Mapping(ruleset)) => {
32+
// Rule 2
33+
match ruleset.get(&Value::String("file".to_string())) {
34+
Some(Value::String(path)) => (),
35+
Some(_) => {
36+
return Err(format!(
37+
"Ruleset {}: field 'file' has invalid type",
38+
name
39+
))
40+
}
41+
_ => {
42+
return Err(format!(
43+
"Ruleset {}: Missing required field 'file'",
44+
name
45+
))
46+
}
47+
}
48+
// Rule 3
49+
if ruleset
50+
.iter()
51+
.filter(|(lhs, rhs)| match (lhs, rhs) {
52+
(Value::String(module_name), Value::Mapping(module_opts)) => true,
53+
_ => false,
54+
})
55+
.collect::<Vec<(&Value, &Value)>>()
56+
.len()
57+
== 0
58+
{
59+
return Err(format!("Ruleset {}: Missing module declarations", name));
60+
}
61+
}
62+
(Value::String(name), _) => {
63+
return Err(format!("Ruleset '{}': invalid value type", name))
64+
}
65+
(_, Value::Mapping(m)) => {
66+
return Err(format!("Ruleset {}: invalid key type", idx))
67+
}
68+
(_, _) => return Err(format!("Ruleset {}: invalid key and value type", idx)),
69+
}
70+
}
71+
return Ok(());
72+
}
73+
_ => Err("Top-level YAML value is not a mapping".to_string()),
74+
}
75+
}

chisel/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod config;
12
mod options;
23
mod run;
34

chisel/src/run.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ use std::error::Error;
22
use std::fs::read_to_string;
33

44
use clap::ArgMatches;
5+
use serde_yaml::Value;
56

6-
use crate::options::ChiselFlags;
7+
use crate::config::validate_config;
78
use crate::fail;
9+
use crate::options::ChiselFlags;
810

911
/// Execute chisel in config-driven mode.
1012
pub fn subcommand_run(flags: ChiselFlags) -> i32 {
@@ -48,7 +50,7 @@ pub fn subcommand_run(flags: ChiselFlags) -> i32 {
4850
println!("Successfully loaded configuration");
4951
}
5052
conf
51-
},
53+
}
5254
Err(e) => fail(
5355
1,
5456
&format!("Failed to load configuration: {}", e.description()),
@@ -62,7 +64,22 @@ pub fn subcommand_run(flags: ChiselFlags) -> i32 {
6264
);
6365
}
6466

65-
// Parse config
67+
let yaml_parsed = serde_yaml::from_str::<Value>(&config).unwrap_or_else(|e| {
68+
fail(
69+
1,
70+
&format!("Failed to parse YAML configuration: {}", e.description()),
71+
)
72+
});
73+
74+
match validate_config(&flags, &yaml_parsed) {
75+
Ok(()) => {
76+
if flags.debug_enabled() {
77+
println!("Config validation OK");
78+
}
79+
}
80+
Err(e) => fail(1, &format!("Config validation failed: {}", e)),
81+
}
82+
6683
// Instantiate modules
6784
// Execute modules
6885
// Display results

0 commit comments

Comments
 (0)