From d75ec22464cba08e3cdd5d2bfa6fae3005d2657e Mon Sep 17 00:00:00 2001 From: zaaarf Date: Mon, 12 Feb 2024 23:26:55 +0100 Subject: [PATCH] chore: improved docs, made api easier --- src/error.rs | 7 +++++++ src/lib.rs | 52 ++++++++++++++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/error.rs b/src/error.rs index a777a8b..bdbb46d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,10 +7,17 @@ pub type Result = StdResult; /// Simple wrapper around the errors that may occur during the program's execution. #[derive(Debug)] pub enum Error { + /// A generic error - you are not supposed to ever actually encounter this, but it beats + /// using a wild unwrap in a library. GenericError(String), + /// Wraps a [`std::io::Errror`] that occurred while reading the files. IoError(std::io::Error), + /// Wraps a [`unic_langid::LanguageIdentifierError`] that occurred while parsing a language + /// identifier. LanguageIdentifierError(unic_langid::LanguageIdentifierError), + /// Wraps any number of [`fluent::FluentError`] that have occurred while parsing. FluentError(Vec), + /// Happens when you try to get a message that does not actually exist. MissingMessageError(String) } diff --git a/src/lib.rs b/src/lib.rs index bf04eeb..5cfd27f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,18 @@ +//! # Fluent, fluently +//! A simple library providing IO for [Project Fluent](https://projectfluent.org/). +//! +//! Sample usage: +//! +//! ```rust +//! let loc = fluent_fluently::Localiser::try_load("./locale".to_string(), "en-US".to_string()).unwrap(); +//! let msg = loc.get_message("hello-world", loc.available_languages.get("it")); +//! println!("{}", msg); +//! ``` +//! +//! The [FluentMessage] you obtained this way will automatically fall back on `en-US` if no locale +//! of the requested type was found. Though, if you want, you `bundles` is a [HashMap], so you can +//! certainly check whether a language is available manually if you so wish. + use std::{collections::HashMap, sync::Arc}; use fluent::{bundle::FluentBundle, FluentResource, FluentMessage}; use intl_memoizer::concurrent::IntlLangMemoizer; @@ -10,10 +25,14 @@ pub mod error; type TypedFluentBundle = FluentBundle, IntlLangMemoizer>; /// The main struct of the program. -/// You can obtain a new instance by calling [Self::try_load()]. +/// You can obtain a new instance by calling [`Self::try_load()`]. pub struct Localiser { - pub bundles: HashMap, - pub default_language: LanguageIdentifier + /// A [HashMap] tying each bundle to its language identifier. + pub bundles: HashMap, + /// A [HashMap] tying each *available* language identifier [String] to an actual [LanguageIdentifier]. + pub available_languages: HashMap, + /// The identifier of the default language. + pub default_language: String } impl Localiser { @@ -26,6 +45,7 @@ impl Localiser { /// forming a single localisation for all intents and purposes. pub fn try_load(path: String, default_language: String) -> Result { let mut bundles = HashMap::new(); + let mut available_languages = HashMap::new(); let paths = std::fs::read_dir(path)? .filter_map(|res| res.ok()) .map(|dir_entry| dir_entry.path()) @@ -37,7 +57,8 @@ impl Localiser { } }).collect::>(); - let default_language = default_language.parse::()?; + // validate default + let default_language = default_language.parse::()?.to_string(); for path in paths { // validate filename as language code @@ -64,11 +85,13 @@ impl Localiser { bundle.add_resource(Self::file_to_resource(&path)?)?; } - bundles.insert(language_code, bundle); + bundles.insert(language_code.to_string(), bundle); + available_languages.insert(language_code.to_string(), language_code); } Ok(Self { bundles, + available_languages, default_language }) } @@ -95,22 +118,11 @@ impl Localiser { } /// Extracts a message from the requested bundle, or from the default one if absent. - pub fn get_message(&self, key: String, language: LanguageIdentifier) -> Result { - let bundle = self.bundles.get(&language) + pub fn get_message(&self, key: String, language: String) -> Result { + self.bundles.get(&language) .or_else(|| self.bundles.get(&self.default_language)) - .ok_or(error::Error::GenericError("Failed to get default bundle! This is not supposed to happen!".to_string()))?; - - bundle.get_message(&key) + .ok_or(error::Error::GenericError("Failed to get default bundle! This is not supposed to happen!".to_string()))? + .get_message(&key) .ok_or(error::Error::MissingMessageError(format!("No such message {} for language {}!", key, language))) } - - /// Returns a [HashMap] tying each [LanguageIdentifier] to its [String] equivalent, to simplify retrieval. - /// Call this as little as possible, as it's rather unoptimised and may scale poorly. - pub fn available_languages(&self) -> HashMap { - let mut res = HashMap::new(); - for lang in self.bundles.keys() { - res.insert(lang.to_string(), lang.clone()); - } - res - } }