milsymbol-rs 0.3.2

A Rust wrapper for the milsymbol JavaScript library to generate military symbols (MIL-STD-2525 and APP-6).
use thiserror::Error;

/// Represents errors that can occur when generating a Milsymbol SVG.
#[derive(Debug, Error)]
pub enum MilsymbolError {
    /// Failed to serialize the provided options to JSON.
    #[error("Failed to serialize options to JSON")]
    SerializationError(#[from] serde_json::Error),

    /// Failed to execute JavaScript within the deno_core runtime.
    #[error("Failed to execute JavaScript execution within deno_core")]
    JsExecutionError(#[source] deno_core::anyhow::Error),

    /// Failed to serialize Rust structs into V8 objects.
    #[error("Could not serialize Rust struct into a V8 object")]
    JsSerializationError(#[source] deno_core::serde_v8::Error),

    /// Failed to deserialize the V8 returned value back into a Rust struct.
    #[error("Could not deserialize the returned V8 value into a Rust struct")]
    JsDeserializationError(#[source] deno_core::serde_v8::Error),

    /// Failed to initialize the Milsymbol JavaScript runtime.
    #[error("Milsymbol JS runtime initialization failed: {0}")]
    RuntimeInitializationError(String),

    /// Invalid CSS color provided for a symbol color.
    #[error("Invalid CSS color '{color}'")]
    InvalidColor {
        /// The invalid color string.
        color: String,
        /// The underlying parsing error.
        #[source]
        source: csscolorparser::ParseColorError,
    },

    /// Failed to render SVG to image.
    #[cfg(feature = "image")]
    #[error("Failed to render SVG to image")]
    ImageRenderingError(#[from] resvg::usvg::Error),

    /// Failed to create pixmap for image rendering.
    #[cfg(feature = "image")]
    #[error("Failed to create pixmap for image rendering")]
    PixmapCreationError,

    /// Failed to create RgbaImage from pixmap data.
    #[cfg(feature = "image")]
    #[error("Failed to create RgbaImage from pixmap data")]
    RgbaImageCreationError,
}

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

    use std::str::FromStr;

    #[test]
    fn test_css_parse_color_error_with_context() {
        let color = "invalid-color-value-should-cause-an-error";
        let parse_err = csscolorparser::Color::from_str(color)
            .err()
            .expect("Should be able to create a parse error");

        let err: eyre::Report = MilsymbolError::InvalidColor {
            color: color.to_string(),
            source: parse_err,
        }
        .into();

        let debug_str = format!("{:?}", err);
        assert!(debug_str.contains("InvalidColor") || debug_str.contains("Invalid CSS color"));
        assert!(debug_str.contains(color));
        // The eyre debug format should contain the source's message
        assert!(debug_str.contains("invalid format") || debug_str.contains("color"));
    }

    #[test]
    fn test_error_chaining_js_execution_error() {
        // Create a nested anyhow error
        let inner_err = deno_core::anyhow::anyhow!("V8 heap allocation failed");
        let nested_err = inner_err.context("Failed to run JavaScript");

        let err: eyre::Report = MilsymbolError::JsExecutionError(nested_err).into();

        let debug_str = format!("{:?}", err);
        assert!(debug_str.contains("Failed to execute JavaScript execution"));
        assert!(debug_str.contains("Failed to run JavaScript"));
        assert!(debug_str.contains("V8 heap allocation failed"));
    }

    #[test]
    fn test_serialization_error_from_serde_json() {
        use serde::ser::Error;
        let serde_err = serde_json::Error::custom("Object too large");
        let err: eyre::Report = MilsymbolError::SerializationError(serde_err).into();

        let debug_str = format!("{:?}", err);
        assert!(debug_str.contains("Failed to serialize options to JSON"));
        assert!(debug_str.contains("Object too large"));
    }

    #[test]
    fn test_js_serialization_error() {
        use serde::ser::Error;
        let serde_err = deno_core::serde_v8::Error::custom("Value out of range");
        let err: eyre::Report = MilsymbolError::JsSerializationError(serde_err).into();

        let debug_str = format!("{:?}", err);
        assert!(debug_str.contains("Could not serialize Rust struct into a V8 object"));
        assert!(debug_str.contains("Value out of range"));
    }
}