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