serdeio 0.6.0

Tiny IO utility library for Rust to serialize/deserialize Serde compatible structs
Documentation
use std::{fmt::Display, path::Path};

use thiserror::Error;

/// Supported data formats for serialization and deserialization.
///
/// This enum represents the various data formats that SerdeIO can handle.
/// Some formats are feature-gated and require enabling specific features in Cargo.toml.
///
/// # Supported Formats
///
/// - `Json`: JSON format (always available)
/// - `JsonLines`: JSON Lines format (always available)
/// - `Csv`: CSV format (requires `csv` feature)
/// - `Yaml`: YAML format (requires `yaml` feature)
/// - `MessagePack`: MessagePack format (requires `messagepack` feature)
/// - `Toml`: TOML format (requires `toml` feature)
///
/// # Examples
///
/// ```rust
/// use serdeio::DataFormat;
///
/// let format = DataFormat::Json;
/// assert_eq!(format.to_string(), "json");
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum DataFormat {
    Auto,
    Json,
    JsonLines,
    #[cfg(feature = "csv")]
    Csv,
    #[cfg(feature = "yaml")]
    Yaml,
    #[cfg(feature = "messagepack")]
    MessagePack,
    #[cfg(feature = "toml")]
    Toml,
}

#[derive(Error, Debug)]
pub enum DataFormatError {
    #[error("Unknown data format: {0}")]
    Unknown(String),
    #[error("No extension found for file: {}", .0.display())]
    NoExtension(std::path::PathBuf),
    #[error("Invalid extension")]
    InvalidExtension,
}

impl TryFrom<&str> for DataFormat {
    type Error = DataFormatError;

    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
        match value.trim().to_lowercase().as_str() {
            "json" => Ok(DataFormat::Json),
            "jsonl" | "jsl" => Ok(DataFormat::JsonLines),
            #[cfg(feature = "csv")]
            "csv" => Ok(DataFormat::Csv),
            #[cfg(feature = "yaml")]
            "yaml" | "yml" => Ok(DataFormat::Yaml),
            #[cfg(feature = "messagepack")]
            "msgpack" | "mpack" | "mpk" => Ok(DataFormat::MessagePack),
            #[cfg(feature = "toml")]
            "toml" => Ok(DataFormat::Toml),
            _ => Err(DataFormatError::Unknown(value.to_string())),
        }
    }
}

impl TryFrom<&Path> for DataFormat {
    type Error = DataFormatError;

    fn try_from(value: &Path) -> std::result::Result<Self, Self::Error> {
        let ext = value
            .extension()
            .ok_or_else(|| DataFormatError::NoExtension(value.to_path_buf()))
            .and_then(|v| v.to_str().ok_or(DataFormatError::InvalidExtension))?;
        Self::try_from(ext)
    }
}

impl Display for DataFormat {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            DataFormat::Auto => write!(f, "auto"),
            DataFormat::Json => write!(f, "json"),
            DataFormat::JsonLines => write!(f, "jsonl"),
            #[cfg(feature = "csv")]
            DataFormat::Csv => write!(f, "csv"),
            #[cfg(feature = "yaml")]
            DataFormat::Yaml => write!(f, "yaml"),
            #[cfg(feature = "messagepack")]
            DataFormat::MessagePack => write!(f, "messagepack"),
            #[cfg(feature = "toml")]
            DataFormat::Toml => write!(f, "toml"),
        }
    }
}

#[cfg(test)]
mod test {
    use std::convert::TryFrom;

    use super::DataFormat;
    #[test]
    fn test_data_format() {
        assert_eq!(DataFormat::try_from("json").unwrap(), DataFormat::Json);
        assert_eq!(
            DataFormat::try_from("jsonl").unwrap(),
            DataFormat::JsonLines
        );
        assert_eq!(DataFormat::try_from("JSON").unwrap(), DataFormat::Json);
        assert_eq!(
            DataFormat::try_from("JSONL").unwrap(),
            DataFormat::JsonLines
        );
        #[cfg(feature = "csv")]
        assert_eq!(DataFormat::try_from("csv").unwrap(), DataFormat::Csv);
        #[cfg(feature = "yaml")]
        assert_eq!(DataFormat::try_from("yaml").unwrap(), DataFormat::Yaml);
        #[cfg(feature = "messagepack")]
        {
            assert_eq!(
                DataFormat::try_from("msgpack").unwrap(),
                DataFormat::MessagePack
            );
            assert_eq!(
                DataFormat::try_from("mpack").unwrap(),
                DataFormat::MessagePack
            );
            assert_eq!(
                DataFormat::try_from("mpk").unwrap(),
                DataFormat::MessagePack
            );
        }
    }
}