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
10
- cargo
11
11
- rustfmt
12
12
- protobuf
13
+
- cargo-nextest
13
14
14
15
steps:
15
16
- name: Format check
16
17
command: cargo fmt --all --check
17
18
- name: Tests
18
-
command: cargo test --workspace --locked
19
+
command: cargo nextest run --workspace --locked --no-fail-fast
20
+
- name: Doc Tests
21
+
command: cargo test --workspace --locked --doc --no-fail-fast
+90
-14
sachy-config/src/lib.rs
+90
-14
sachy-config/src/lib.rs
···
1
+
use core::{error::Error, fmt::Debug, str::FromStr};
1
2
use std::io::Write;
2
3
3
-
use miette::{IntoDiagnostic, Result, miette};
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
+
}
4
27
5
28
pub fn output_config<W: Write>(config: &str, mut writer: W) -> Result<()> {
6
29
let output = parse_config(config)?;
···
55
78
)
56
79
})?;
57
80
58
-
let constant_output = format!(
59
-
"pub const {}: {} = {};\n",
60
-
name,
61
-
kind,
62
-
value.clone().decorated("", "")
63
-
);
64
-
output.push_str(&constant_output);
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 {}, use [statics] for these", name, kind),
95
+
_ => bail!("Unsupported type: {}", kind),
96
+
};
97
+
98
+
output.push_str(&line);
99
+
65
100
Ok(())
66
101
})?;
67
102
···
77
112
#[test]
78
113
fn it_parses_config() -> Result<()> {
79
114
let input = r#"[constants]
80
-
FIRST = { type = "u8", value = 2 }
81
-
VALUE = { type = "f32", value = 42.0 }
115
+
FIRST = { type = "u8", value = "2" }
116
+
VALUE = { type = "f32", value = "42.0" }
82
117
[statics]
83
118
EXAMPLE = "thing"
84
119
"#;
···
94
129
Ok(())
95
130
}
96
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_disallows_string_constants() {
162
+
let input = r#"[constants]
163
+
FIRST = { type = "&str", value = "42" }
164
+
"#;
165
+
166
+
let expected = "FIRST is a &str, use [statics] for these";
167
+
168
+
let output = parse_config(input).expect_err("Output somehow successfully parsed");
169
+
170
+
assert_eq!(output.to_string(), expected);
171
+
}
172
+
97
173
#[test]
98
174
fn it_handles_only_statics() -> Result<()> {
99
175
let input = r#"[statics]
···
112
188
#[test]
113
189
fn it_handles_only_constants() -> Result<()> {
114
190
let input = r#"[constants]
115
-
FIRST = { type = "u8", value = 42 }
191
+
FIRST = { type = "u8", value = "42" }
116
192
"#;
117
193
118
194
let expected = "pub const FIRST: u8 = 42;\n";
···
149
225
#[test]
150
226
fn it_outputs_to_file() -> Result<()> {
151
227
let input = r#"[constants]
152
-
FIRST = { type = "u8", value = 2 }
153
-
VALUE = { type = "u64", value = 42 }
228
+
FIRST = { type = "u128", value = "340282366920938463463374607431768211455" }
229
+
VALUE = { type = "u64", value = "42" }
154
230
[statics]
155
231
EXAMPLE = "thing"
156
232
"#;
157
233
let expected = r#"pub static EXAMPLE: &str = "thing";
158
-
pub const FIRST: u8 = 2;
234
+
pub const FIRST: u128 = 340282366920938463463374607431768211455;
159
235
pub const VALUE: u64 = 42;
160
236
"#;
161
237
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
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.