fluent-static-codegen 0.6.1

Automatically generate Rust functions from Fluent message files for streamlined localization in Rust applications.
Documentation
use std::{cell::RefCell, collections::BTreeSet, fmt::Display, ops::Deref, rc::Rc};

use fluent_syntax::ast;
use syn::Ident;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FluentId(Rc<str>);

impl FluentId {
    pub fn new<S: ToString>(s: &S) -> Self {
        Self(Rc::from(s.to_string()))
    }

    pub fn join(&self, id: impl Into<FluentId>) -> Self {
        Self(Rc::from(format!("{}.{}", self.0, id.into().0)))
    }
}

impl Deref for FluentId {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl AsRef<str> for FluentId {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl std::borrow::Borrow<str> for FluentId {
    fn borrow(&self) -> &str {
        self.0.borrow()
    }
}

impl Display for FluentId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl<S: ToString> From<&ast::Identifier<S>> for FluentId {
    fn from(value: &ast::Identifier<S>) -> Self {
        Self::new(&value.name)
    }
}

impl<S: ToString> From<&ast::Attribute<S>> for FluentId {
    fn from(value: &ast::Attribute<S>) -> Self {
        Self::from(&value.id)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PublicFluentId(FluentId);

impl PublicFluentId {
    pub fn new(s: String) -> Self {
        Self(FluentId::new(&s))
    }
}

impl From<FluentId> for PublicFluentId {
    fn from(value: FluentId) -> Self {
        Self(value.clone())
    }
}

impl std::borrow::Borrow<str> for PublicFluentId {
    fn borrow(&self) -> &str {
        self.0.borrow()
    }
}

impl Display for PublicFluentId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

#[derive(Debug, Clone)]
struct FluentMessageAttrs {
    id: FluentId,
    private: bool,
    fn_ident: Ident,
    vars: Vec<FluentVariable>,
    var_idents: BTreeSet<Ident>,
    unique_vars: BTreeSet<FluentVariable>,
}

#[derive(Debug, Clone)]
pub struct FluentMessage {
    attrs: Rc<RefCell<FluentMessageAttrs>>,
}

impl FluentMessage {
    pub fn new(id: impl Into<FluentId>, fn_ident: Ident, private: bool) -> Self {
        let attrs = Rc::new(RefCell::new(FluentMessageAttrs {
            id: id.into(),
            fn_ident,
            private,
            vars: Vec::new(),
            var_idents: BTreeSet::new(),
            unique_vars: BTreeSet::new(),
        }));
        Self { attrs }
    }

    pub fn id(&self) -> FluentId {
        self.attrs.borrow().id.clone()
    }

    pub fn fn_ident(&self) -> Ident {
        self.attrs.borrow().fn_ident.clone()
    }

    pub fn is_private(&self) -> bool {
        self.attrs.borrow().private
    }

    pub fn add_var(&self, var: FluentVariable) {
        let mut attrs = self.attrs.borrow_mut();
        if !attrs.unique_vars.contains(&var) {
            attrs.unique_vars.insert(var.clone());
            attrs.var_idents.insert(var.var_ident.clone());
            attrs.vars.push(var);
        }
    }

    pub fn has_vars(&self) -> bool {
        let attrs = self.attrs.borrow();
        !attrs.unique_vars.is_empty()
    }

    pub fn declared_vars(&self) -> Vec<FluentVariable> {
        let attrs = self.attrs.borrow();
        attrs.vars.iter().cloned().collect()
    }

    pub fn vars(&self) -> BTreeSet<FluentVariable> {
        let attrs = self.attrs.borrow();
        attrs.vars.iter().cloned().collect()
    }

    pub fn public_id(&self) -> PublicFluentId {
        let attrs = self.attrs.borrow();
        if attrs.unique_vars.is_empty() {
            attrs.id.clone().into()
        } else {
            let vars = attrs
                .unique_vars
                .iter()
                .map(|var| var.var_name.as_str())
                .collect::<Vec<&str>>()
                .join(",");

            PublicFluentId::new(format!("{}={}", attrs.id, vars))
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FluentVariable {
    pub var_ident: Ident,
    pub var_name: String,
}

impl FluentVariable {
    pub fn new(var_name: String, var_ident: Ident) -> Self {
        Self {
            var_ident,
            var_name,
        }
    }
}