caber 0.1.1

Embed binary data into JavaScript and TypeScript projects.
Documentation
#[cfg(feature = "cli")]
use std::str::FromStr;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum CaberError {
    #[error("invalid export mode provided. Expected default, object, or none")]
    InvalidExportMode,
    #[error(
        "unrecognized output language requested. Expected JavaScript / ECMAScript or TypeScript"
    )]
    UnrecognizedLanguage,
}

#[cfg_attr(feature = "cli", derive(clap::Parser))]
#[derive(Debug, Clone, Copy)]
pub enum ExportMode {
    Default,
    Object,
    None,
}

impl Default for ExportMode {
    fn default() -> Self {
        Self::Default
    }
}

#[cfg(feature = "cli")]
impl FromStr for ExportMode {
    type Err = CaberError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "default" => Ok(Self::Default),
            "object" => Ok(Self::Object),
            "none" => Ok(Self::None),
            _ => Err(Self::Err::InvalidExportMode),
        }
    }
}

#[derive(Default, Clone, Copy)]
pub struct CaberConfig {
    pub export_mode: ExportMode,
}

#[derive(Default, Clone, Copy)]
pub struct Binary<'a> {
    bytes: &'a [u8],
    config: CaberConfig,
}

impl<'a> Binary<'a> {
    pub fn new(bytes: &'a [u8]) -> Self {
        Self {
            bytes,
            config: CaberConfig::default(),
        }
    }

    pub fn with_config(mut self, config: CaberConfig) -> Self {
        self.config = config;
        self
    }

    pub fn to_javascript(&self) -> String {
        self.to_ecmascript()
    }

    pub fn to_typescript(&self) -> String {
        self.to_ecmascript()
    }

    pub fn to_ecmascript(&self) -> String {
        let mut array_innards = String::new();
        for (byte_index, byte) in self.bytes.iter().enumerate() {
            let mut element = format!("{}", byte.to_string());
            if byte_index != self.bytes.len() - 1 {
                element.push_str(", ")
            }
            array_innards.push_str(&element);
        }
        self.export_text(array_innards)
    }

    fn export_text(&self, array_innards: String) -> String {
        match self.config.export_mode {
            ExportMode::Default => format!(
                "const binary = new Uint8Array([{}]);\r\nexport default binary;",
                array_innards
            ),
            ExportMode::Object => format!(
                "const binary = new Uint8Array([{}]);\r\nexport {{ binary }};",
                array_innards
            ),
            ExportMode::None => format!("const binary = new Uint8Array([{}]);", array_innards),
        }
    }
}

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

    #[test]
    fn to_ecmascript() {
        let bin = Binary::new(&[0, 1, 2, 3]);
        assert_eq!(
            bin.to_ecmascript(),
            String::from("const binary = new Uint8Array([0, 1, 2, 3]);\r\nexport default binary;")
        )
    }
}