cloudiful-bevy-localization 0.1.2

Reusable Bevy localization runtime built around app-provided static registries.
Documentation
use crate::{LocalizationDefinition, LocalizationLoadError};
use std::collections::HashMap;
use toml::Value;

pub(crate) fn load_tables(
    definition: &'static LocalizationDefinition,
) -> Result<HashMap<&'static str, HashMap<String, String>>, LocalizationLoadError> {
    let mut tables = definition
        .locales()
        .into_iter()
        .map(|locale| (locale.id(), HashMap::new()))
        .collect::<HashMap<_, _>>();

    for source in definition.sources {
        let value = toml::from_str::<Value>(source.contents)?;
        let table = tables.get_mut(source.locale).ok_or_else(|| {
            LocalizationLoadError::InvalidData(format!(
                "locale source '{}' is not listed in available locales",
                source.locale
            ))
        })?;
        flatten_locale_value(source.namespace, &value, table)?;
    }

    Ok(tables)
}

fn flatten_locale_value(
    prefix: &str,
    value: &Value,
    texts: &mut HashMap<String, String>,
) -> Result<(), LocalizationLoadError> {
    match value {
        Value::String(text) => {
            let previous = texts.insert(prefix.to_string(), text.clone());
            if previous.is_some() {
                return Err(LocalizationLoadError::InvalidData(format!(
                    "duplicate localization key: {prefix}"
                )));
            }
        }
        Value::Table(table) => {
            for (key, nested_value) in table {
                let nested_prefix = format!("{prefix}.{key}");
                flatten_locale_value(&nested_prefix, nested_value, texts)?;
            }
        }
        _ => {
            return Err(LocalizationLoadError::InvalidData(format!(
                "localization value at '{prefix}' must be a string or table"
            )));
        }
    }

    Ok(())
}