local_fmt_macros_internal/parse/
mod.rs

1use std::str::FromStr;
2
3use proc_macro2::TokenStream;
4use quote::ToTokens;
5use syn::Ident;
6
7mod alloc;
8pub use alloc::*;
9
10mod static_ref;
11pub use static_ref::*;
12
13pub enum MessageTokenValue {
14    StaticText(String),
15    PlaceholderArg(usize),
16
17    // in const, it's expected to be a &'static str
18    // in not const, it's expected to be a String
19    PlaceholderIdent(Ident),
20}
21
22impl MessageTokenValue {
23    pub fn to_alloc_token_stream(&self) -> TokenStream {
24        match self {
25            MessageTokenValue::StaticText(s) => {
26                quote::quote! {
27                    local_fmt::AllocMessageFormat::AllocText(#s.to_string()),
28                }
29            }
30            MessageTokenValue::PlaceholderArg(n) => {
31                let n = *n;
32                quote::quote! {
33                    local_fmt::AllocMessageFormat::Placeholder(#n),
34                }
35            }
36            MessageTokenValue::PlaceholderIdent(ident) => {
37                quote::quote! {
38                    local_fmt::AllocMessageFormat::AllocText(#ident),
39                }
40            }
41        }
42    }
43
44    pub fn to_static_token_stream(&self) -> TokenStream {
45        match self {
46            MessageTokenValue::StaticText(s) => {
47                let s = s.as_str();
48                quote::quote! {
49                    local_fmt::RefMessageFormat::RefText(#s),
50                }
51            }
52            MessageTokenValue::PlaceholderArg(n) => {
53                let n = *n;
54                quote::quote! {
55                    local_fmt::RefMessageFormat::Placeholder(#n),
56                }
57            }
58            MessageTokenValue::PlaceholderIdent(ident) => {
59                quote::quote! {
60                    local_fmt::RefMessageFormat::RefText(#ident),
61                }
62            }
63        }
64    }
65}
66
67#[derive(Debug, thiserror::Error)]
68pub enum MessageValueError {
69    #[error("Placeholder number {0} is not found in the message. The hiest number found is {1}")]
70    NotFound(usize, usize),
71    #[error("not found placeholder value in braces")]
72    EmptyPlaceholder,
73}
74
75pub trait MessageValue: ToTokens + Sized {
76    const MESSAGE_IDENT: &'static str;
77    const MESSAGE_ARG_WRAPPER: &'static str;
78
79    fn as_arg(&self) -> Option<usize>;
80
81    fn new_string(s: String) -> Self;
82
83    fn new_placeholder_raw(s: &str) -> Result<Self, MessageValueError>;
84}
85
86pub struct MessageToken<V: MessageValue> {
87    pub values: Vec<V>,
88    pub placeholder_max: Option<usize>,
89}
90
91impl<V: MessageValue> MessageToken<V> {
92    pub fn args(&self) -> usize {
93        self.placeholder_max.map_or(0, |v| v + 1)
94    }
95
96    pub fn new(values: Vec<V>) -> Result<Self, MessageValueError> {
97        let max = values.iter().filter_map(|v| v.as_arg()).max();
98
99        if let Some(max) = max {
100            let mut flag = vec![false; max + 1];
101            for v in &values {
102                if let Some(n) = v.as_arg() {
103                    flag[n] = true;
104                }
105            }
106            for (i, v) in flag.iter().enumerate() {
107                if !v {
108                    return Err(MessageValueError::NotFound(i, max));
109                }
110            }
111        }
112
113        Ok(Self {
114            values,
115            placeholder_max: max,
116        })
117    }
118}
119
120impl<V: MessageValue> ToTokens for MessageToken<V> {
121    fn to_tokens(&self, tokens: &mut TokenStream) {
122        let count = self.args();
123        let values = self
124            .values
125            .iter()
126            .map(|v| v.to_token_stream())
127            .collect::<Vec<TokenStream>>();
128
129        let ident = Ident::new(V::MESSAGE_IDENT, proc_macro2::Span::call_site());
130        let wrapper = TokenStream::from_str(V::MESSAGE_ARG_WRAPPER).unwrap();
131
132        let token = quote::quote! {
133            unsafe { local_fmt::#ident::<#count>::new_unchecked(#wrapper[
134                #(
135                    #values
136                )*
137            ]) }
138        };
139
140        tokens.extend(token);
141    }
142}
143
144impl<V: MessageValue> FromStr for MessageToken<V> {
145    type Err = MessageValueError;
146
147    fn from_str(s: &str) -> Result<Self, Self::Err> {
148        let mut values = Vec::<V>::new();
149
150        let mut buffer = Vec::<u8>::new();
151
152        let mut bytes = s.bytes();
153
154        while let Some(byte) = bytes.next() {
155            match byte {
156                b'{' => {
157                    if !buffer.is_empty() {
158                        let s = unsafe { String::from_utf8_unchecked(std::mem::take(&mut buffer)) };
159                        values.push(V::new_string(s));
160                    }
161
162                    let mut placeholder = Vec::new();
163
164                    loop {
165                        match bytes.next() {
166                            Some(byte) => match byte {
167                                b'}' => {
168                                    if placeholder.is_empty() {
169                                        return Err(MessageValueError::EmptyPlaceholder);
170                                    }
171                                    let placeholder =
172                                        unsafe { std::str::from_utf8_unchecked(&placeholder) };
173                                    values.push(V::new_placeholder_raw(placeholder)?);
174                                    break;
175                                }
176                                byte => placeholder.push(byte),
177                            },
178                            None => {
179                                return Err(MessageValueError::EmptyPlaceholder);
180                            }
181                        }
182                    }
183                }
184                b'\\' => {
185                    if let Some(byte) = bytes.next() {
186                        match byte {
187                            b'{' => buffer.push(b'{'),
188                            _ => {
189                                buffer.push(b'\\');
190                                buffer.push(byte);
191                            }
192                        }
193                    } else {
194                        buffer.push(b'\\');
195                    }
196                }
197                _ => buffer.push(byte),
198            }
199        }
200
201        if !buffer.is_empty() {
202            let s = unsafe { String::from_utf8_unchecked(buffer) };
203            values.push(V::new_string(s));
204        }
205
206        Self::new(values)
207    }
208}