use std::fmt;
use rskit_errors::{AppError, AppResult};
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json::Value;
pub trait Codec: fmt::Debug + Send + Sync + 'static {
fn name(&self) -> &'static str;
fn encode_value(&self, value: &Value) -> AppResult<String>;
fn decode_value(&self, contents: &str) -> AppResult<Value>;
}
pub fn encode<T>(codec: &dyn Codec, value: &T) -> AppResult<String>
where
T: Serialize + ?Sized,
{
let value = serde_json::to_value(value).map_err(|err| {
AppError::invalid_input("codec", "failed to convert value into the value model")
.with_cause(err)
})?;
codec.encode_value(&value)
}
pub fn decode<T>(codec: &dyn Codec, contents: &str) -> AppResult<T>
where
T: DeserializeOwned,
{
let value = codec.decode_value(contents)?;
T::deserialize(value).map_err(|err| {
AppError::invalid_input("codec", "failed to deserialize value").with_cause(err)
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::JsonCodec;
use serde::Deserialize;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Settings {
name: String,
retries: u32,
}
#[test]
fn encode_then_decode_round_trips_typed_value() {
let settings = Settings {
name: "svc".to_owned(),
retries: 3,
};
let encoded = encode(&JsonCodec::default(), &settings).unwrap();
let decoded: Settings = decode(&JsonCodec::default(), &encoded).unwrap();
assert_eq!(decoded, settings);
}
#[test]
fn encode_reports_unrepresentable_values() {
use std::collections::HashMap;
let map: HashMap<(i32, i32), i32> = HashMap::from([((1, 2), 3)]);
let err = encode(&JsonCodec::default(), &map).unwrap_err();
assert!(err.to_string().contains("value model"));
}
#[test]
fn decode_reports_type_mismatches() {
let err = decode::<Settings>(&JsonCodec::default(), "[1, 2, 3]").unwrap_err();
assert!(err.to_string().contains("deserialize"));
}
#[test]
fn decode_propagates_codec_parse_errors() {
let err = decode::<Settings>(&JsonCodec::default(), "{ not json").unwrap_err();
assert!(err.to_string().contains("parse"));
}
}