1use std::fmt;
2
3use rskit_errors::{AppError, AppResult};
4use serde::Serialize;
5use serde::de::DeserializeOwned;
6use serde_json::Value;
7
8pub trait Codec: fmt::Debug + Send + Sync + 'static {
15 fn name(&self) -> &'static str;
17
18 fn encode_value(&self, value: &Value) -> AppResult<String>;
24
25 fn decode_value(&self, contents: &str) -> AppResult<Value>;
31}
32
33pub fn encode<T>(codec: &dyn Codec, value: &T) -> AppResult<String>
42where
43 T: Serialize + ?Sized,
44{
45 let value = serde_json::to_value(value).map_err(|err| {
46 AppError::invalid_input("codec", "failed to convert value into the value model")
47 .with_cause(err)
48 })?;
49 codec.encode_value(&value)
50}
51
52pub fn decode<T>(codec: &dyn Codec, contents: &str) -> AppResult<T>
62where
63 T: DeserializeOwned,
64{
65 let value = codec.decode_value(contents)?;
66 T::deserialize(value).map_err(|err| {
67 AppError::invalid_input("codec", "failed to deserialize value").with_cause(err)
68 })
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::JsonCodec;
75 use serde::Deserialize;
76
77 #[derive(Debug, PartialEq, Serialize, Deserialize)]
78 struct Settings {
79 name: String,
80 retries: u32,
81 }
82
83 #[test]
84 fn encode_then_decode_round_trips_typed_value() {
85 let settings = Settings {
86 name: "svc".to_owned(),
87 retries: 3,
88 };
89
90 let encoded = encode(&JsonCodec::default(), &settings).unwrap();
91 let decoded: Settings = decode(&JsonCodec::default(), &encoded).unwrap();
92
93 assert_eq!(decoded, settings);
94 }
95
96 #[test]
97 fn encode_reports_unrepresentable_values() {
98 use std::collections::HashMap;
99
100 let map: HashMap<(i32, i32), i32> = HashMap::from([((1, 2), 3)]);
103 let err = encode(&JsonCodec::default(), &map).unwrap_err();
104
105 assert!(err.to_string().contains("value model"));
106 }
107
108 #[test]
109 fn decode_reports_type_mismatches() {
110 let err = decode::<Settings>(&JsonCodec::default(), "[1, 2, 3]").unwrap_err();
112
113 assert!(err.to_string().contains("deserialize"));
114 }
115
116 #[test]
117 fn decode_propagates_codec_parse_errors() {
118 let err = decode::<Settings>(&JsonCodec::default(), "{ not json").unwrap_err();
119
120 assert!(err.to_string().contains("parse"));
121 }
122}