Skip to main content

rskit_codec/
toml.rs

1use rskit_errors::{AppError, AppResult};
2use serde_json::Value;
3
4use crate::codec::Codec;
5
6/// Built-in TOML codec.
7///
8/// Decodes TOML into the canonical [`Value`] tree and encodes a value tree back
9/// to TOML. The top-level value must be a table (TOML has no top-level scalar or
10/// array document), and JSON `null` has no TOML representation — both surface as
11/// typed errors rather than panics.
12#[derive(Debug, Clone, Copy, Default)]
13pub struct TomlCodec;
14
15impl Codec for TomlCodec {
16    fn name(&self) -> &'static str {
17        "toml"
18    }
19
20    fn encode_value(&self, value: &Value) -> AppResult<String> {
21        toml::to_string(value).map_err(|err| {
22            AppError::invalid_input("codec", "failed to serialize value as TOML").with_cause(err)
23        })
24    }
25
26    fn decode_value(&self, contents: &str) -> AppResult<Value> {
27        toml::from_str::<Value>(contents)
28            .map_err(|err| AppError::invalid_input("codec", "failed to parse TOML").with_cause(err))
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn round_trips_table() {
38        let codec = TomlCodec;
39        let value: Value = serde_json::json!({
40            "name": "svc",
41            "retries": 3,
42            "nested": { "enabled": true }
43        });
44
45        let encoded = codec.encode_value(&value).unwrap();
46        let decoded = codec.decode_value(&encoded).unwrap();
47
48        assert_eq!(decoded, value);
49        assert_eq!(codec.name(), "toml");
50    }
51
52    #[test]
53    fn rejects_malformed_input() {
54        let err = TomlCodec.decode_value("= not valid").unwrap_err();
55        assert!(err.to_string().contains("parse"));
56    }
57
58    #[test]
59    fn rejects_unrepresentable_values() {
60        // JSON `null` has no TOML representation; this must surface as a typed
61        // error rather than a panic.
62        let err = TomlCodec.encode_value(&Value::Null).unwrap_err();
63        assert!(err.to_string().contains("serialize"));
64    }
65}