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 #[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>, },
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#[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#[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}