Current section
Files
Jump to
Current section
Files
native/fluent_native/src/lib.rs
extern crate rustler;
use rustler::{Encoder, Env, Error, Term, TermType};
use rustler::ResourceArc;
use std::sync::RwLock;
use fluent::{FluentBundle, FluentValue, FluentResource, FluentArgs};
use unic_langid::LanguageIdentifier;
mod atoms {
rustler::atoms! {
ok,
error,
bad_locale,
bad_resource,
bad_msg,
no_value,
use_isolating,
}
}
struct Container {
data: RwLock<FluentBundle<FluentResource>>,
}
fn on_init<'a>(env: Env<'a>, _load_info: Term<'a>) -> bool {
rustler::resource!(Container, env);
true
}
#[rustler::nif]
fn init<'a>(env: Env<'a>, lang: String, bundle_init_keyword: Vec<(rustler::types::Atom, Term)>) -> Result<Term<'a>, Error> {
// Getting language
let lang_id = match lang.parse::<LanguageIdentifier>() {
Ok(lang_id) => lang_id,
Err(_e) => return Ok((atoms::error(), (atoms::bad_locale(), lang)).encode(env))
};
// Initializing bundle
let mut bundle = FluentBundle::new(&[lang_id]);
// Setting different configs and flags
for (key, value) in bundle_init_keyword {
if key == atoms::use_isolating() {
let use_isolating_value: bool = value.decode()?;
bundle.set_use_isolating(use_isolating_value);
}
}
let bundle = Container {
data: RwLock::new(bundle),
};
Ok((atoms::ok(), ResourceArc::new(bundle)).encode(env))
}
#[rustler::nif]
fn with_resource<'a>(env: Env<'a>, container: ResourceArc<Container>, source: String) -> Result<Term<'a>, Error> {
// Initializing resource
let resource = match FluentResource::try_new(source) {
Ok(resource) => resource,
Err((_resource, _error)) => {
return Ok(
(atoms::error(), atoms::bad_resource()).encode(env)
)
}
};
// Locking bundle to write
let mut bundle = container.data.write().unwrap();
match bundle.add_resource(resource) {
Ok(_value) => Ok(atoms::ok().encode(env)),
Err(_e) => return Ok((atoms::error(), atoms::bad_resource()).encode(env))
}
}
#[rustler::nif]
fn format_pattern<'a>(
env: Env<'a>,
container: ResourceArc<Container>,
msg_id: String,
arg_ids: Vec<(rustler::types::Atom, Term)>
) -> Result<Term<'a>, Error> {
// Reconfiguring args
let arg_ids: Vec<(String, FluentValue)> = arg_ids.into_iter().map(|(key, value)| {
let fluent_value: FluentValue = match &value.get_type() {
TermType::Binary => {
match value.decode::<String>() {
Ok(string) => FluentValue::from(string),
Err(_e) => panic!("Mismatched types")
}
},
TermType::Number => {
match value.decode::<f64>() {
Ok(float) => FluentValue::from(float),
Err(_e) => {
match value.decode::<i64>() {
Ok(integer) => FluentValue::from(integer),
Err(_e) => panic!("Mismatched types")
}
}
}
}
_ => panic!("Mismatched types") // TODO: Add error handling
};
(format!("{:?}", key), fluent_value)
}).collect();
// Locking bundle to write
let bundle = container.data.read().unwrap();
// Getting message
let msg = match bundle.get_message(&msg_id) {
Some(msg) => msg,
None => return Ok((atoms::error(), atoms::bad_msg()).encode(env))
};
// Getting args
let mut args = FluentArgs::new();
for (key, value) in &arg_ids {
args.insert(key, value.clone());
}
// Getting errors
let mut errors = vec![];
// Formatting pattern
let pattern = match msg.value {
Some(value) => value,
None => return Ok((atoms::error(), atoms::no_value()).encode(env))
};
let value = bundle.format_pattern(&pattern, Some(&args), &mut errors);
Ok((atoms::ok(), value.into_owned()).encode(env))
}
#[rustler::nif]
fn assert_locale<'a>(env: Env<'a>, lang: String) -> Result<Term<'a>, Error> {
// Getting language
match lang.parse::<LanguageIdentifier>() {
Ok(lang_id) => Ok((atoms::ok(), &lang_id.to_string()).encode(env)),
Err(_e) => Ok((atoms::error(), (atoms::bad_locale(), lang)).encode(env))
}
}
rustler::init!(
"Elixir.Fluent.Native",
[init, with_resource, format_pattern, assert_locale],
load = on_init
);