burgerlingual 1.2.0

burger utility library for web localisation
Documentation
use language_tags::LanguageTag;
use std::collections::HashMap;
#[cfg(feature = "json")]
use std::fs::File;
#[cfg(feature = "json")]
use std::path::Path;
use std::string::String;
#[cfg(feature = "json")]
use std::fmt::Display;
#[cfg(feature = "json")]
use std::fmt::Formatter;
#[cfg(feature = "json")]
use std::error::Error;

#[derive(Debug, PartialEq, Clone)]
pub struct Translation {
    language: LanguageTag,
    translations: HashMap<String, String>,
}

#[cfg(feature = "json")]
#[derive(Debug)]
pub enum TranslationError {
    FileHasNoStem,
    FailedToReadFile(std::io::Error),
    FailedToParseLanguageTag(language_tags::ParseError),
    FailedToCanonicalizeLanguageTag(language_tags::ValidationError),
    FailedToParseJSON(serde_json::Error),
    FileStemNotValidUTF8,
}

#[cfg(feature = "json")]
impl Display for TranslationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            TranslationError::FileHasNoStem => write!(f, "file has no stem"),
            TranslationError::FailedToReadFile(error) => write!(f, "failed to read file: {}", error),
            TranslationError::FailedToParseLanguageTag(error) => write!(f, "failed to parse language tag: {}", error),
            TranslationError::FailedToCanonicalizeLanguageTag(error) => write!(f, "failed to canonicalize language tag: {}", error),
            TranslationError::FailedToParseJSON(error) => write!(f, "failed to parse JSON: {}", error),
            TranslationError::FileStemNotValidUTF8 => write!(f, "file has invalid UTF-8"),
        }
    }
}

#[cfg(feature = "json")]
impl Error for TranslationError {}

impl Translation {
    #[must_use]
    pub const fn new(language: LanguageTag, translations: HashMap<String, String>) -> Self {
        Self { language, translations }
    }

    #[allow(clippy::must_use_candidate)]
    pub const fn language(&self) -> &LanguageTag {
        &self.language
    }

    #[allow(clippy::must_use_candidate)]
    pub fn completeness(&self, canonical: &Self) -> usize {
        self.translations.keys().filter(|key|
            canonical.translations.contains_key(*key)
        ).count()
    }

    pub fn get_translation(&self, key: &str) -> Option<&str> {
        self.translations.get(key).map(String::as_str)
    }

    #[cfg(feature = "json")]
    pub fn parse_language_tag(tag: &str) -> Result<LanguageTag, TranslationError> {
        LanguageTag::parse(tag)
            .map_err(TranslationError::FailedToParseLanguageTag)?
            .canonicalize()
            .map_err(TranslationError::FailedToCanonicalizeLanguageTag)
    }

    #[cfg(feature = "json")]
    pub fn from_json(language: LanguageTag, json: &str) -> Result<Self, TranslationError> {
        Ok(Self {
            language,
            translations: serde_json::from_str(json).map_err(TranslationError::FailedToParseJSON)?,
        })
    }

    #[cfg(feature = "json")]
    pub fn from_json_file<P: AsRef<Path>>(path: P) -> Result<Self, TranslationError> {
        let file = File::open(path.as_ref()).map_err(TranslationError::FailedToReadFile)?;
        Ok(Self {
            language: Self::parse_language_tag(if let Some(stem) = path.as_ref().file_stem() {
                if let Some(stem) = stem.to_str() {
                    stem
                } else {
                    return Err(TranslationError::FileStemNotValidUTF8);
                }
            } else {
                return Err(TranslationError::FileHasNoStem);
            })?,
            translations: serde_json::from_reader(file).map_err(TranslationError::FailedToParseJSON)?,
        })
    }
}