mf1_parser/
ast.rs

1use icu_plurals::PluralCategory;
2use serde::{Deserialize, Serialize};
3use std::borrow::Cow;
4use std::collections::{HashMap, HashSet};
5use std::ops::Deref;
6
7#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
8#[serde(tag = "type")]
9#[serde(bound(
10    serialize = "StrMaybeOwned: Serialize",
11    deserialize = "StrMaybeOwned: Deserialize<'de>"
12))]
13pub enum Token<'a, 'b, StrMaybeOwned>
14where
15    StrMaybeOwned: Deref<Target = str> + Clone,
16{
17    #[serde(rename = "content")]
18    Content { value: StrMaybeOwned },
19
20    #[serde(rename = "argument")]
21    PlainArg { arg: StrMaybeOwned },
22    #[serde(rename = "function")]
23    FunctionArg {
24        arg: StrMaybeOwned,
25        key: StrMaybeOwned,
26        param: Option<Cow<'b, [Token<'a, 'b, StrMaybeOwned>]>>,
27    },
28
29    //   type: 'plural' | 'select' | 'selectordinal';
30    #[serde(rename = "plural")]
31    Plural {
32        arg: StrMaybeOwned,
33        cases: Cow<'b, [PluralCase<'a, 'b, StrMaybeOwned>]>,
34        #[serde(default, rename = "pluralOffset")]
35        plural_offset: Option<i32>,
36    },
37    #[serde(rename = "select")]
38    Select {
39        arg: StrMaybeOwned,
40        cases: Cow<'b, [SelectCase<'a, 'b, StrMaybeOwned>]>,
41        #[serde(default, rename = "pluralOffset")]
42        plural_offset: Option<i32>, // TODO: Warn about this - it's unusable
43    },
44    #[serde(rename = "selectordinal")]
45    SelectOrdinal {
46        arg: StrMaybeOwned,
47        cases: Cow<'b, [PluralCase<'a, 'b, StrMaybeOwned>]>,
48        #[serde(default, rename = "pluralOffset")]
49        plural_offset: Option<i32>,
50    },
51
52    #[serde(rename = "octothorpe")]
53    Octothorpe {},
54}
55
56// #[serde_as]
57#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
58#[serde(bound(
59    serialize = "StrMaybeOwned: Serialize",
60    deserialize = "StrMaybeOwned: Deserialize<'de>"
61))]
62pub struct PluralCase<'a, 'b, StrMaybeOwned>
63where
64    StrMaybeOwned: Deref<Target = str> + Clone,
65{
66    pub key: PluralCategory,
67    pub tokens: Cow<'b, [Token<'a, 'b, StrMaybeOwned>]>,
68}
69
70impl<'a, 'b, StrMaybeOwned> TryFrom<SelectCase<'a, 'b, StrMaybeOwned>>
71    for PluralCase<'a, 'b, StrMaybeOwned>
72where
73    StrMaybeOwned: Deref<Target = str> + Clone,
74{
75    type Error = ();
76
77    fn try_from(value: SelectCase<'a, 'b, StrMaybeOwned>) -> Result<Self, Self::Error> {
78        Ok(PluralCase {
79            key: PluralCategory::get_for_cldr_bytes(value.key.as_bytes()).ok_or(())?,
80            tokens: value.tokens,
81        })
82    }
83}
84
85// #[serde_as]
86#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
87#[serde(bound(
88    serialize = "StrMaybeOwned: Serialize",
89    deserialize = "StrMaybeOwned: Deserialize<'de>"
90))]
91pub struct SelectCase<'a, 'b, StrMaybeOwned>
92where
93    StrMaybeOwned: 'a + Deref<Target = str> + Clone,
94{
95    pub key: StrMaybeOwned,
96    pub tokens: Cow<'b, [Token<'a, 'b, StrMaybeOwned>]>,
97}
98
99#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
100pub enum ArgType {
101    OrdinalArg,
102    PlainArg,
103    SelectArg,
104    FunctionArg,
105}
106
107pub trait TokenSlice<'a, T> {
108    fn get_args(&'a self) -> HashMap<&'a T, HashSet<ArgType>>
109    where
110        T: Deref<Target = str> + Clone,
111    {
112        let mut args = HashMap::new();
113
114        self.get_args_into(&mut args);
115        args
116    }
117    fn get_args_into(&'a self, args: &mut HashMap<&'a T, HashSet<ArgType>>)
118    where
119        T: Deref<Target = str> + Clone;
120}
121
122impl<'a, T> TokenSlice<'a, T> for [Token<'_, 'a, T>]
123where
124    T: Deref<Target = str>
125        + Clone
126        + for<'b> std::cmp::PartialEq<&'b str>
127        + std::cmp::Eq
128        + std::hash::Hash,
129{
130    fn get_args_into(&'a self, args: &mut HashMap<&'a T, HashSet<ArgType>>)
131    where
132        T: Deref<Target = str> + Clone,
133    {
134        for t in self.iter() {
135            match t {
136                Token::Content { value: _ } => {}
137                Token::PlainArg { arg } => {
138                    args.entry(arg).or_default().insert(ArgType::PlainArg);
139                }
140                Token::FunctionArg {
141                    arg,
142                    key: _,
143                    param: _,
144                } => {
145                    args.entry(arg).or_default().insert(ArgType::FunctionArg);
146                }
147                Token::Plural {
148                    arg,
149                    cases,
150                    plural_offset: _,
151                }
152                | Token::SelectOrdinal {
153                    arg,
154                    cases,
155                    plural_offset: _,
156                } => {
157                    args.entry(arg).or_default().insert(ArgType::OrdinalArg);
158                    for case in cases.iter() {
159                        case.tokens.get_args_into(args)
160                    }
161                }
162                Token::Select {
163                    arg,
164                    cases,
165                    plural_offset: _,
166                } => {
167                    args.entry(arg).or_default().insert(ArgType::SelectArg);
168                    for case in cases.iter() {
169                        case.tokens.get_args_into(args)
170                    }
171                }
172                Token::Octothorpe {} => {}
173            };
174        }
175    }
176}