1use num_derive::{FromPrimitive, ToPrimitive};
2use num_traits::FromPrimitive;
3use serde::de::{Error, Visitor};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt::{Display, Formatter};
6use std::mem;
7use std::ops::{Deref, DerefMut};
8use std::str::FromStr;
9
10pub const SPECIAL_MATH: [&str; 49] = [
12 "alpha", "Alpha", "beta", "Beta", "gamma", "Gamma", "delta", "Delta", "epsilon", "Epsilon",
13 "zeta", "Zeta", "eta", "Eta", "theta", "Theta", "iota", "Iota", "kappa", "Kappa", "lambda",
14 "Lambda", "mu", "Mu", "nu", "Nu", "xi", "Xi", "omicron", "Omicron", "phi", "Phi", "rho", "Rho",
15 "sigma", "Sigma", "tau", "Tau", "upsilon", "Upsilon", "phi", "Phi", "chi", "Chi", "psi", "Psi",
16 "omega", "Omega", "quote",
17];
18
19#[derive(Debug, Clone, Copy, Default)]
21pub struct StringSpan {
22 pub start: usize,
24 pub end: usize,
26}
27
28impl Display for StringSpan {
29 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
30 write!(f, "({}:{})", self.start, self.end)
31 }
32}
33
34#[derive(Debug, Clone)]
36pub struct ParseError {
37 pub span: StringSpan,
39 pub kind: ParseErrorKind,
41}
42
43impl Display for ParseError {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 write!(f, "error at {}: {}", self.span, self.kind)
46 }
47}
48
49impl std::error::Error for ParseError {}
50
51#[derive(Debug, Clone)]
53pub enum ParseErrorKind {
54 SpecialNotRecognised(String),
56 NestedIndex,
58 UnclosedSpecialTag(String),
60}
61
62impl Display for ParseErrorKind {
63 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
64 match self {
65 Self::SpecialNotRecognised(special) => write!(f, "special '{special}' not recognised"),
66 Self::NestedIndex => write!(f, "nested index is not valid"),
67 Self::UnclosedSpecialTag(special) => {
68 write!(f, "unclosed special tag. Parsed '{special}'")
69 }
70 }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
76pub enum MathIndex {
77 Normal,
79 Lower,
81}
82
83impl Display for MathIndex {
84 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
85 match self {
86 Self::Normal => write!(f, "}}"),
87 Self::Lower => write!(f, "_{{"),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
94pub enum MathChar {
95 Ascii(char),
97 Special(MathSpecial),
99 SetIndex(MathIndex),
101 Prime,
103}
104
105impl Display for MathChar {
106 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107 match self {
108 Self::Ascii(c) => write!(f, "{c}"),
109 Self::Special(c) => write!(f, "[{c}]"),
110 Self::SetIndex(x) => write!(f, "{x}"),
111 Self::Prime => write!(f, "'"),
112 }
113 }
114}
115
116#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive, PartialEq, Eq, Serialize)]
118pub enum MathSpecial {
119 Alpha,
121 AlphaUpper,
123 Beta,
125 BetaUpper,
127 Gamma,
129 GammaUpper,
131 Delta,
133 DeltaUpper,
135 Epsilon,
137 EpsilonUpper,
139 Zeta,
141 ZetaUpper,
143 Eta,
145 EtaUpper,
147 Theta,
149 ThetaUpper,
151 Iota,
153 IotaUpper,
155 Kappa,
157 KappaUpper,
159 Lambda,
161 LambdaUpper,
163 Mu,
165 MuUpper,
167 Nu,
169 NuUpper,
171 Xi,
173 XiUpper,
175 Omicron,
177 OmicronUpper,
179 Pi,
181 PiUpper,
183 Rho,
185 RhoUpper,
187 Sigma,
189 SigmaUpper,
191 Tau,
193 TauUpper,
195 Upsilon,
197 UpsilonUpper,
199 Phi,
201 PhiUpper,
203 Chi,
205 ChiUpper,
207 Psi,
209 PsiUpper,
211 Omega,
213 OmegaUpper,
215 Quote,
217}
218
219impl MathSpecial {
220 #[must_use]
222 pub fn is_alphabetic(self) -> bool {
223 self != Self::Quote
224 }
225
226 #[must_use]
231 pub fn parse(char_code: &str) -> Option<Self> {
232 SPECIAL_MATH
233 .iter()
234 .enumerate()
235 .find(|x| *x.1 == char_code)
236 .map(|x| MathSpecial::from_usize(x.0).unwrap())
237 }
238}
239
240impl Display for MathSpecial {
241 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
242 write!(f, "{}", SPECIAL_MATH[*self as usize])
243 }
244}
245
246#[derive(Debug, Clone, Default)]
248pub struct MathString(Vec<MathChar>);
249
250impl MathString {
251 #[must_use]
253 pub fn new() -> Self {
254 Self(Vec::new())
255 }
256
257 #[must_use]
259 pub fn is_empty(&self) -> bool {
260 self.0.is_empty()
261 }
262
263 #[must_use]
265 pub fn raw(source: &str) -> Self {
266 Self(source.chars().map(MathChar::Ascii).collect())
267 }
268}
269
270impl Deref for MathString {
271 type Target = Vec<MathChar>;
272
273 fn deref(&self) -> &Self::Target {
274 &self.0
275 }
276}
277
278impl DerefMut for MathString {
279 fn deref_mut(&mut self) -> &mut Self::Target {
280 &mut self.0
281 }
282}
283
284impl Display for MathString {
285 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
286 for c in &self.0 {
287 write!(f, "{c}")?;
288 }
289
290 Ok(())
291 }
292}
293
294impl FromStr for MathString {
295 type Err = ParseError;
296
297 fn from_str(content: &str) -> Result<Self, Self::Err> {
298 let mut ignore_next = false;
299 let mut math_string = Vec::new();
300 let mut indexed = false;
301 let mut collect_special = false;
302 let mut special = String::new();
303 let mut index_delimited = false;
304
305 let mut special_start = 0;
306
307 for (char_count, c) in content.chars().enumerate() {
308 if collect_special {
309 if ignore_next || (c != ']' && c != '\\') {
310 special.push(c);
311 ignore_next = false;
312 } else if c == ']' {
313 math_string.push(MathChar::Special(MathSpecial::parse(&special).ok_or_else(
314 || ParseError {
315 span: StringSpan {
316 start: special_start + 1,
317 end: char_count,
318 },
319 kind: ParseErrorKind::SpecialNotRecognised(mem::take(&mut special)),
320 },
321 )?));
322
323 collect_special = false;
325 } else {
326 ignore_next = true;
327 }
328 } else if ignore_next {
329 math_string.push(MathChar::Ascii(c));
330 ignore_next = false;
331 } else if c == '\\' {
332 ignore_next = true;
333 } else if c == '_' {
334 if indexed {
335 return Err(ParseError {
336 span: StringSpan {
337 start: 0,
338 end: content.len(),
339 },
340 kind: ParseErrorKind::NestedIndex,
341 });
342 }
343
344 math_string.push(MathChar::SetIndex(MathIndex::Lower));
345 indexed = true;
346 } else if c == '[' {
347 special_start = char_count;
348 collect_special = true;
349 } else if c == ' ' {
350 if indexed && !index_delimited {
351 indexed = false;
352 math_string.push(MathChar::SetIndex(MathIndex::Normal));
353 }
354
355 math_string.push(MathChar::Ascii(c));
356 } else if c == '{'
357 && math_string.last().copied() == Some(MathChar::SetIndex(MathIndex::Lower))
358 {
359 index_delimited = true;
360 } else if c == '}' && index_delimited {
361 indexed = false;
362 index_delimited = false;
363 math_string.push(MathChar::SetIndex(MathIndex::Normal));
364 } else if c == '\'' {
365 math_string.push(MathChar::Prime);
366 } else {
367 math_string.push(MathChar::Ascii(c));
368 }
369 }
370
371 if indexed {
372 math_string.push(MathChar::SetIndex(MathIndex::Normal));
373 }
374
375 if collect_special {
376 return Err(ParseError {
378 span: StringSpan {
379 start: special_start,
380 end: content.len(),
381 },
382 kind: ParseErrorKind::UnclosedSpecialTag(special),
383 });
384 }
385
386 Ok(Self(math_string))
387 }
388}
389
390impl Serialize for MathString {
391 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
392 where
393 S: Serializer,
394 {
395 let mut s = String::new();
396
397 for c in &self.0 {
398 match c {
399 MathChar::Prime => s.push('c'),
400 MathChar::SetIndex(MathIndex::Normal) => s.push('}'),
401 MathChar::SetIndex(MathIndex::Lower) => s += "_{",
402 MathChar::Ascii(c) => s.extend(['\\', *c]),
403 MathChar::Special(c) => {
404 s.push('[');
405 s += SPECIAL_MATH[*c as usize];
406 s.push(']');
407 }
408 }
409 }
410
411 serializer.serialize_str(&self.to_string())
412 }
413}
414
415impl<'de> Deserialize<'de> for MathString {
416 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
417 where
418 D: Deserializer<'de>,
419 {
420 deserializer.deserialize_str(MathStringVisitor)
421 }
422}
423
424struct MathStringVisitor;
425
426impl Visitor<'_> for MathStringVisitor {
427 type Value = MathString;
428
429 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
430 formatter.write_str("a valid math string")
431 }
432
433 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
434 where
435 E: Error,
436 {
437 MathString::from_str(v).map_err(|err| E::custom(err))
438 }
439}