pub use serde::{Deserialize, Serialize};
pub fn to_string<T: Serialize>(value: &T) -> Result<String, ron::Error> {
ron::to_string(value)
}
pub fn to_string_pretty<T: Serialize>(value: &T) -> Result<String, ron::Error> {
ron::ser::to_string_pretty(value, ron::ser::PrettyConfig::default())
}
pub fn to_writer<W: std::io::Write, T: Serialize>(
mut writer: W,
value: &T,
) -> Result<(), ron::Error> {
let serialized = ron::to_string(value)?;
writer.write_all(serialized.as_bytes()).map_err(|error| {
ron::Error::Message(format!(
"write_all failed after {} bytes: {error}",
serialized.len()
))
})
}
pub fn from_str<T: serde::de::DeserializeOwned>(text: &str) -> Result<T, ron::error::SpannedError> {
ron::from_str(text)
}
pub fn from_slice<T: serde::de::DeserializeOwned>(bytes: &[u8]) -> Result<T, FromSliceError> {
let text = std::str::from_utf8(bytes).map_err(FromSliceError::Utf8)?;
ron::from_str(text).map_err(FromSliceError::Ron)
}
#[derive(Debug)]
#[non_exhaustive]
pub enum FromSliceError {
Utf8(std::str::Utf8Error),
Ron(ron::error::SpannedError),
}
impl core::fmt::Display for FromSliceError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
Self::Utf8(ref err) => write!(f, "RON input is not valid UTF-8: {err}"),
Self::Ron(ref err) => write!(f, "{err}"),
}
}
}
impl std::error::Error for FromSliceError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Self::Utf8(ref err) => Some(err),
Self::Ron(ref err) => Some(err),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Point {
x: i32,
y: i32,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
enum Shape {
Circle(u32),
Rect { w: u32, height: u32 },
}
#[test]
fn struct_roundtrips_through_string() -> Result<(), Box<dyn std::error::Error>> {
let point = Point { x: 1, y: 2 };
let text = to_string(&point)?;
let restored: Point = from_str(&text)?;
assert_eq!(point, restored);
Ok(())
}
#[test]
fn enum_roundtrips() -> Result<(), Box<dyn std::error::Error>> {
let shapes = vec![Shape::Circle(5), Shape::Rect { w: 10, height: 20 }];
for shape in &shapes {
let text = to_string(shape)?;
let restored: Shape = from_str(&text)?;
assert_eq!(*shape, restored);
}
Ok(())
}
#[test]
fn pretty_output_has_indentation() -> Result<(), Box<dyn std::error::Error>> {
let point = Point { x: 1, y: 2 };
let text = to_string_pretty(&point)?;
assert!(text.contains('\n'));
Ok(())
}
#[test]
fn from_str_rejects_invalid() {
let result: Result<Point, _> = from_str("{{{");
assert!(result.is_err());
}
#[test]
fn from_slice_rejects_invalid_utf8() {
let result: Result<Point, _> = from_slice(&[0xFF, 0xFE]);
assert!(matches!(result, Err(FromSliceError::Utf8(_))));
}
}