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