herolib_otoml 0.3.13

OTOML - Canonical TOML serialization format with compact binary representation.
Documentation
//! Serialization extension trait for OTOML types.
//!
//! Provides convenient methods for serializing/deserializing types to/from
//! OTOML and JSON formats.
//!
//! # Example
//!
//! ```rust,ignore
//! use herolib_otoml::otoml::OtomlSerialize;
//!
//! #[derive(Serialize, Deserialize)]
//! struct User {
//!     name: String,
//!     email: String,
//! }
//!
//! let user = User {
//!     name: "Alice".to_string(),
//!     email: "alice@example.com".to_string(),
//! };
//!
//! // Serialize to OTOML
//! let otoml = user.to_otoml()?;
//!
//! // Deserialize from OTOML
//! let loaded = User::from_otoml(&otoml)?;
//!
//! // Serialize to JSON
//! let json = user.to_json()?;
//!
//! // Deserialize from JSON
//! let loaded = User::from_json(&json)?;
//! ```

use super::canonical::{dump_otoml, load_otoml};
use super::error::{OtomlError, Result};
use serde::{Serialize, de::DeserializeOwned};

/// Extension trait providing OTOML and JSON serialization methods.
///
/// This trait is automatically implemented for any type that implements
/// `Serialize + DeserializeOwned`.
pub trait OtomlSerialize: Serialize + DeserializeOwned + Sized {
    /// Serialize to OTOML format string.
    ///
    /// Returns a canonical OTOML string with "O:\n" prefix.
    fn to_otoml(&self) -> Result<String> {
        dump_otoml(self)
    }

    /// Deserialize from OTOML format string.
    ///
    /// The input must start with "O:\n" prefix.
    fn from_otoml(s: &str) -> Result<Self> {
        load_otoml(s)
    }

    /// Serialize to JSON format string.
    ///
    /// Returns a compact JSON string.
    fn to_json(&self) -> Result<String> {
        serde_json::to_string(self).map_err(|e| OtomlError::JsonSerialize(e.to_string()))
    }

    /// Serialize to pretty-printed JSON format string.
    ///
    /// Returns an indented JSON string for readability.
    fn to_json_pretty(&self) -> Result<String> {
        serde_json::to_string_pretty(self).map_err(|e| OtomlError::JsonSerialize(e.to_string()))
    }

    /// Deserialize from JSON format string.
    fn from_json(s: &str) -> Result<Self> {
        serde_json::from_str(s).map_err(|e| OtomlError::JsonDeserialize(e.to_string()))
    }
}

/// Blanket implementation for all types with Serialize + DeserializeOwned.
impl<T> OtomlSerialize for T where T: Serialize + DeserializeOwned {}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::Deserialize;

    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
    struct TestUser {
        name: String,
        age: u32,
    }

    #[test]
    fn test_otoml_roundtrip() {
        let user = TestUser {
            name: "Alice".to_string(),
            age: 30,
        };

        let otoml = user.to_otoml().unwrap();
        assert!(otoml.starts_with("O:\n"));

        let loaded = TestUser::from_otoml(&otoml).unwrap();
        assert_eq!(user, loaded);
    }

    #[test]
    fn test_json_roundtrip() {
        let user = TestUser {
            name: "Bob".to_string(),
            age: 25,
        };

        let json = user.to_json().unwrap();
        assert!(json.contains("\"name\":\"Bob\""));

        let loaded = TestUser::from_json(&json).unwrap();
        assert_eq!(user, loaded);
    }

    #[test]
    fn test_json_pretty() {
        let user = TestUser {
            name: "Charlie".to_string(),
            age: 35,
        };

        let json = user.to_json_pretty().unwrap();
        assert!(json.contains('\n'));
        assert!(json.contains("  ")); // indentation
    }
}