Skip to main content

es_fluent_shared/
namer.rs

1//! This module provides types for naming Fluent keys and documentation.
2
3use derive_more::{Debug, Deref, Display};
4use heck::ToSnakeCase as _;
5use quote::format_ident;
6
7#[derive(Clone, Debug, Deref, Display, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Serialize)]
8pub struct FluentKey(pub String);
9
10impl From<String> for FluentKey {
11    fn from(s: String) -> Self {
12        Self(s)
13    }
14}
15
16impl From<&str> for FluentKey {
17    fn from(s: &str) -> Self {
18        Self::from(s.to_string())
19    }
20}
21
22impl From<&syn::Ident> for FluentKey {
23    fn from(ident: &syn::Ident) -> Self {
24        Self(ident.to_string().to_snake_case())
25    }
26}
27
28impl FluentKey {
29    pub const DELIMITER: &str = "-";
30    pub const THIS_SUFFIX: &str = "_this";
31
32    pub fn join(&self, suffix: impl std::fmt::Display) -> Self {
33        let suffix_str = suffix.to_string();
34        if suffix_str.is_empty() {
35            self.clone()
36        } else {
37            Self(format!("{}{}{}", self.0, Self::DELIMITER, suffix_str))
38        }
39    }
40
41    pub fn new_this(ftl_name: &syn::Ident) -> Self {
42        let this_ident = quote::format_ident!("{}{}", ftl_name, Self::THIS_SUFFIX);
43        Self::from(&this_ident)
44    }
45}
46
47impl quote::ToTokens for FluentKey {
48    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
49        let key_string = &self.0;
50        tokens.extend(quote::quote! { #key_string });
51    }
52}
53
54#[derive(Clone, Debug, Deref, Display, Eq, Hash, PartialEq)]
55pub struct FluentDoc(String);
56
57impl From<&FluentKey> for FluentDoc {
58    fn from(ftl_key: &FluentKey) -> Self {
59        FluentDoc(format!("Key = `{}`", *ftl_key))
60    }
61}
62
63impl quote::ToTokens for FluentDoc {
64    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
65        let doc_string = &self.0;
66        tokens.extend(quote::quote! { #doc_string });
67    }
68}
69
70pub struct UnnamedItem(usize);
71
72impl std::fmt::Display for UnnamedItem {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        write!(f, "{}{}", Self::UNNAMED_PREFIX, self.0)
75    }
76}
77
78impl UnnamedItem {
79    const UNNAMED_PREFIX: &str = "f";
80
81    pub fn to_ident(&self) -> syn::Ident {
82        format_ident!("{}", self.to_string())
83    }
84}
85
86impl From<usize> for UnnamedItem {
87    fn from(index: usize) -> Self {
88        Self(index)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use quote::quote;
96
97    #[test]
98    fn fluent_key_conversions_and_joining_work() {
99        let from_string = FluentKey::from("hello_world".to_string());
100        let from_str = FluentKey::from("hello_world");
101        let from_ident = FluentKey::from(&syn::Ident::new(
102            "HelloWorld",
103            proc_macro2::Span::call_site(),
104        ));
105
106        assert_eq!(from_string.to_string(), "hello_world");
107        assert_eq!(from_str.to_string(), "hello_world");
108        assert_eq!(from_ident.to_string(), "hello_world");
109
110        assert_eq!(from_ident.join("suffix").to_string(), "hello_world-suffix");
111        assert_eq!(from_ident.join("").to_string(), "hello_world");
112    }
113
114    #[test]
115    fn fluent_key_this_and_token_generation_work() {
116        let this_key =
117            FluentKey::new_this(&syn::Ident::new("MyType", proc_macro2::Span::call_site()));
118        assert_eq!(this_key.to_string(), "my_type_this");
119
120        let tokens = quote!(#this_key).to_string();
121        assert!(tokens.contains("my_type_this"));
122    }
123
124    #[test]
125    fn fluent_doc_and_unnamed_item_cover_display_and_tokens() {
126        let key = FluentKey::from("field_name");
127        let doc = FluentDoc::from(&key);
128        let doc_tokens = quote!(#doc).to_string();
129        assert!(doc_tokens.contains("Key ="));
130        assert!(doc_tokens.contains("field_name"));
131
132        let unnamed = UnnamedItem::from(3);
133        assert_eq!(unnamed.to_string(), "f3");
134        assert_eq!(unnamed.to_ident().to_string(), "f3");
135    }
136}