use std::sync::Mutex;
use intl_memoizer::{IntlLangMemoizer, Memoizable};
use unic_langid::LanguageIdentifier;
use fluent_bundle::types::{FluentType, FluentValue};
use fluent_bundle::{FluentArgs, FluentBundle, FluentResource};
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
enum DateTimeStyleValue {
Full,
Long,
Medium,
Short,
None,
}
impl std::default::Default for DateTimeStyleValue {
fn default() -> Self {
Self::None
}
}
impl<'l> From<&FluentValue<'l>> for DateTimeStyleValue {
fn from(input: &FluentValue) -> Self {
if let FluentValue::String(s) = input {
match s.as_ref() {
"full" => Self::Full,
"long" => Self::Long,
"medium" => Self::Medium,
"short" => Self::Short,
_ => Self::None,
}
} else {
Self::None
}
}
}
#[derive(Debug, PartialEq, Eq, Default, Clone, Hash)]
struct DateTimeOptions {
pub date_style: DateTimeStyleValue,
pub time_style: DateTimeStyleValue,
}
impl DateTimeOptions {
pub fn merge(&mut self, input: &FluentArgs) {
for (key, value) in input {
match *key {
"dateStyle" => self.date_style = value.into(),
"timeStyle" => self.time_style = value.into(),
_ => {}
}
}
}
}
impl<'l> From<&FluentArgs<'l>> for DateTimeOptions {
fn from(input: &FluentArgs) -> Self {
let mut opts = Self::default();
opts.merge(input);
opts
}
}
#[derive(Debug, PartialEq, Clone)]
struct DateTime {
epoch: usize,
options: DateTimeOptions,
}
impl DateTime {
pub fn new(epoch: usize, options: DateTimeOptions) -> Self {
Self { epoch, options }
}
}
impl FluentType for DateTime {
fn duplicate(&self) -> Box<dyn FluentType> {
Box::new(DateTime::new(self.epoch, DateTimeOptions::default()))
}
fn as_string(&self, intls: &Mutex<IntlLangMemoizer>) -> std::borrow::Cow<'static, str> {
let mut intls = intls
.lock()
.expect("Failed to get rw lock on intl memoizer.");
let dtf = intls
.try_get::<DateTimeFormatter>((self.options.clone(),))
.expect("Failed to retrieve a formatter.");
dtf.format(self.epoch).into()
}
}
struct DateTimeFormatter {
lang: LanguageIdentifier,
options: DateTimeOptions,
}
impl DateTimeFormatter {
pub fn new(lang: LanguageIdentifier, options: DateTimeOptions) -> Result<Self, ()> {
Ok(Self { lang, options })
}
pub fn format(&self, epoch: usize) -> String {
format!(
"My Custom Formatted Date from epoch: {}, in locale: {}, using options: {:#?}",
epoch, self.lang, self.options
)
}
}
impl Memoizable for DateTimeFormatter {
type Args = (DateTimeOptions,);
type Error = ();
fn construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error> {
Self::new(lang, args.0)
}
}
fn main() {
let ftl_string = String::from(
r#"
key-date = Today is { DATETIME($epoch, dateStyle: "long", timeStyle: "short") }
"#,
);
let res = FluentResource::try_new(ftl_string).expect("Could not parse an FTL string.");
let lang: LanguageIdentifier = "en".parse().unwrap();
let mut bundle = FluentBundle::new(&[lang]);
bundle
.add_resource(res)
.expect("Failed to add FTL resources to the bundle.");
bundle
.add_function("DATETIME", |positional, named| match positional.get(0) {
Some(FluentValue::Number(n)) => {
let epoch = n.value as usize;
let options = named.into();
FluentValue::Custom(Box::new(DateTime::new(epoch, options)))
}
_ => FluentValue::None,
})
.expect("Failed to add a function.");
bundle.set_use_isolating(false);
let msg = bundle
.get_message("key-date")
.expect("Failed to retrieve the message.");
let pattern = msg.value.expect("Message has no value.");
let mut errors = vec![];
let mut args = FluentArgs::new();
let epoch: u64 = 1580127760093;
args.insert("epoch", epoch.into());
let value = bundle.format_pattern(pattern, Some(&args), &mut errors);
println!("{}", value);
}