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 {
16 fn name(&self) -> &'static str;
18
19 fn encode_value(&self, value: &Value) -> AppResult<String>;
26
27 fn decode_value(&self, contents: &str) -> AppResult<Value>;
34}
35
36pub fn encode<T>(codec: &dyn Codec, value: &T) -> AppResult<String>
45where
46 T: Serialize + ?Sized,
47{
48 let value = serde_json::to_value(value).map_err(|err| {
49 AppError::invalid_input("codec", "failed to convert value into the value model")
50 .with_cause(err)
51 })?;
52 codec.encode_value(&value)
53}
54
55pub fn decode<T>(codec: &dyn Codec, contents: &str) -> AppResult<T>
66where
67 T: DeserializeOwned,
68{
69 let value = codec.decode_value(contents)?;
70 T::deserialize(value).map_err(|err| {
71 AppError::invalid_input("codec", "failed to deserialize value").with_cause(err)
72 })
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::JsonCodec;
79 use serde::Deserialize;
80
81 #[derive(Debug, PartialEq, Serialize, Deserialize)]
82 struct Settings {
83 name: String,
84 retries: u32,
85 }
86
87 #[test]
88 fn encode_then_decode_round_trips_typed_value() {
89 let settings = Settings {
90 name: "svc".to_owned(),
91 retries: 3,
92 };
93
94 let encoded = encode(&JsonCodec::default(), &settings).unwrap();
95 let decoded: Settings = decode(&JsonCodec::default(), &encoded).unwrap();
96
97 assert_eq!(decoded, settings);
98 }
99
100 #[test]
101 fn encode_reports_unrepresentable_values() {
102 use std::collections::HashMap;
103
104 let map: HashMap<(i32, i32), i32> = HashMap::from([((1, 2), 3)]);
107 let err = encode(&JsonCodec::default(), &map).unwrap_err();
108
109 assert!(err.to_string().contains("value model"));
110 }
111
112 #[test]
113 fn decode_reports_type_mismatches() {
114 let err = decode::<Settings>(&JsonCodec::default(), "[1, 2, 3]").unwrap_err();
116
117 assert!(err.to_string().contains("deserialize"));
118 }
119
120 #[test]
121 fn decode_propagates_codec_parse_errors() {
122 let err = decode::<Settings>(&JsonCodec::default(), "{ not json").unwrap_err();
123
124 assert!(err.to_string().contains("parse"));
125 }
126}