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.
+4
-1
.tangled/workflows/test.yml
+4
-1
.tangled/workflows/test.yml
···
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
+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
1 commit
expand
collapse
feat: Config format tweak with stronger type validation
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.
2/2 success
expand
collapse
expand 0 comments
pull request successfully merged
sachy.dev
submitted
#0
1 commit
expand
collapse
feat: Config format tweak with stronger type validation
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.