Repo of no-std crates for my personal embedded projects

feat: Config format tweak with stronger type validation #8

merged opened by sachy.dev targeting main from format-validate

Fixes the config format to require values to be strings, so to avoid problems with toml Value not being able to accommodate certain types, but then adds parsing according to the declared type to ensure the values are valid before being formatted into constants.

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:usjm3ynnir6y4inkcdovrfei/sh.tangled.repo.pull/3md646poqio22
+81 -15
Diff #0
+4 -1
.tangled/workflows/test.yml
··· 10 - cargo 11 - rustfmt 12 - protobuf 13 14 steps: 15 - name: Format check 16 command: cargo fmt --all --check 17 - name: Tests 18 - command: cargo test --workspace --locked
··· 10 - cargo 11 - rustfmt 12 - protobuf 13 + - cargo-nextest 14 15 steps: 16 - name: Format check 17 command: cargo fmt --all --check 18 - name: Tests 19 + command: cargo nextest run --workspace --locked --no-fail-fast 20 + - name: Doc Tests 21 + command: cargo test --workspace --locked --doc --no-fail-fast
+77 -14
sachy-config/src/lib.rs
··· 1 use std::io::Write; 2 3 - use miette::{IntoDiagnostic, Result, miette}; 4 5 pub fn output_config<W: Write>(config: &str, mut writer: W) -> Result<()> { 6 let output = parse_config(config)?; ··· 55 ) 56 })?; 57 58 - let constant_output = format!( 59 - "pub const {}: {} = {};\n", 60 - name, 61 - kind, 62 - value.clone().decorated("", "") 63 - ); 64 - output.push_str(&constant_output); 65 Ok(()) 66 })?; 67 ··· 77 #[test] 78 fn it_parses_config() -> Result<()> { 79 let input = r#"[constants] 80 - FIRST = { type = "u8", value = 2 } 81 - VALUE = { type = "f32", value = 42.0 } 82 [statics] 83 EXAMPLE = "thing" 84 "#; ··· 94 Ok(()) 95 } 96 97 #[test] 98 fn it_handles_only_statics() -> Result<()> { 99 let input = r#"[statics] ··· 112 #[test] 113 fn it_handles_only_constants() -> Result<()> { 114 let input = r#"[constants] 115 - FIRST = { type = "u8", value = 42 } 116 "#; 117 118 let expected = "pub const FIRST: u8 = 42;\n"; ··· 149 #[test] 150 fn it_outputs_to_file() -> Result<()> { 151 let input = r#"[constants] 152 - FIRST = { type = "u8", value = 2 } 153 - VALUE = { type = "u64", value = 42 } 154 [statics] 155 EXAMPLE = "thing" 156 "#; 157 let expected = r#"pub static EXAMPLE: &str = "thing"; 158 - pub const FIRST: u8 = 2; 159 pub const VALUE: u64 = 42; 160 "#; 161 let output: Vec<u8> = Vec::new();
··· 1 + use core::{error::Error, fmt::Debug, str::FromStr}; 2 use std::io::Write; 3 4 + use miette::{Context, IntoDiagnostic, Result, bail, miette}; 5 + use toml_edit::Value; 6 + 7 + fn validate_kind_to_string<T: Debug + FromStr + Send + Sync>( 8 + name: &str, 9 + kind: &str, 10 + value: &Value, 11 + ) -> Result<String> 12 + where 13 + T::Err: Send + Sync + Error + 'static, 14 + { 15 + value 16 + .as_str() 17 + .map_or_else( 18 + || Err(miette!("{} is not a value", value)), 19 + |value_str| { 20 + value_str.parse().into_diagnostic().wrap_err_with(|| { 21 + format!("Parsing failure of constant \"{}\" as {}", name, kind) 22 + }) 23 + }, 24 + ) 25 + .map(|value: T| format!("pub const {}: {} = {:?};\n", name, kind, value)) 26 + } 27 28 pub fn output_config<W: Write>(config: &str, mut writer: W) -> Result<()> { 29 let output = parse_config(config)?; ··· 78 ) 79 })?; 80 81 + let line = match kind { 82 + "u8" => validate_kind_to_string::<u8>(name, kind, value)?, 83 + "i8" => validate_kind_to_string::<i8>(name, kind, value)?, 84 + "u16" => validate_kind_to_string::<u16>(name, kind, value)?, 85 + "i16" => validate_kind_to_string::<i16>(name, kind, value)?, 86 + "u32" => validate_kind_to_string::<u32>(name, kind, value)?, 87 + "i32" => validate_kind_to_string::<i32>(name, kind, value)?, 88 + "u64" => validate_kind_to_string::<u64>(name, kind, value)?, 89 + "i64" => validate_kind_to_string::<i64>(name, kind, value)?, 90 + "u128" => validate_kind_to_string::<u128>(name, kind, value)?, 91 + "i128" => validate_kind_to_string::<i128>(name, kind, value)?, 92 + "f32" => validate_kind_to_string::<f32>(name, kind, value)?, 93 + "f64" => validate_kind_to_string::<f64>(name, kind, value)?, 94 + "&str" => bail!("{} is a string, use [statics] for these", kind), 95 + _ => bail!("Unsupported type: {}", kind), 96 + }; 97 + 98 + output.push_str(&line); 99 + 100 Ok(()) 101 })?; 102 ··· 112 #[test] 113 fn it_parses_config() -> Result<()> { 114 let input = r#"[constants] 115 + FIRST = { type = "u8", value = "2" } 116 + VALUE = { type = "f32", value = "42.0" } 117 [statics] 118 EXAMPLE = "thing" 119 "#; ··· 129 Ok(()) 130 } 131 132 + #[test] 133 + fn it_validates_value_types() -> Result<()> { 134 + let input = r#"[constants] 135 + FIRST = { type = "u8", value = "1234" } 136 + VALUE = { type = "f32", value = "42.0" } 137 + [statics] 138 + EXAMPLE = "thing" 139 + "#; 140 + 141 + let output = parse_config(input); 142 + 143 + let report = output.expect_err("Output was somehow successful"); 144 + 145 + let mut error_chain = report.chain(); 146 + 147 + assert_eq!( 148 + "Parsing failure of constant \"FIRST\" as u8", 149 + error_chain.next().unwrap().to_string() 150 + ); 151 + assert_eq!( 152 + "number too large to fit in target type", 153 + error_chain.next().unwrap().to_string() 154 + ); 155 + assert!(error_chain.next().is_none()); 156 + 157 + Ok(()) 158 + } 159 + 160 #[test] 161 fn it_handles_only_statics() -> Result<()> { 162 let input = r#"[statics] ··· 175 #[test] 176 fn it_handles_only_constants() -> Result<()> { 177 let input = r#"[constants] 178 + FIRST = { type = "u8", value = "42" } 179 "#; 180 181 let expected = "pub const FIRST: u8 = 42;\n"; ··· 212 #[test] 213 fn it_outputs_to_file() -> Result<()> { 214 let input = r#"[constants] 215 + FIRST = { type = "u128", value = "340282366920938463463374607431768211455" } 216 + VALUE = { type = "u64", value = "42" } 217 [statics] 218 EXAMPLE = "thing" 219 "#; 220 let expected = r#"pub static EXAMPLE: &str = "thing"; 221 + pub const FIRST: u128 = 340282366920938463463374607431768211455; 222 pub const VALUE: u64 = 42; 223 "#; 224 let output: Vec<u8> = Vec::new();

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
feat: Config format tweak with stronger type validation
2/2 success
expand
expand 0 comments
pull request successfully merged
sachy.dev submitted #0
1 commit
expand
feat: Config format tweak with stronger type validation
2/2 success
expand
expand 0 comments