1use std::convert::TryFrom;
2
3use serde::{Deserialize, Serialize};
4use typed_builder::TypedBuilder;
5
6use crate::error::{Error, ErrorKind};
7
8#[serde_with::skip_serializing_none]
12#[derive(Clone, Debug, Default, Serialize, Deserialize, TypedBuilder)]
13#[serde(rename_all = "camelCase")]
14#[builder(field_defaults(default, setter(into)))]
15#[non_exhaustive]
16pub struct Collation {
17 #[builder(!default)]
21 pub locale: String,
22
23 pub strength: Option<CollationStrength>,
25
26 pub case_level: Option<bool>,
28
29 pub case_first: Option<CollationCaseFirst>,
31
32 pub numeric_ordering: Option<bool>,
34
35 pub alternate: Option<CollationAlternate>,
38
39 pub max_variable: Option<CollationMaxVariable>,
42
43 pub normalization: Option<bool>,
45
46 pub backwards: Option<bool>,
48}
49
50#[derive(Debug, Clone, Copy)]
52#[non_exhaustive]
53pub enum CollationStrength {
54 Primary,
59
60 Secondary,
65
66 Tertiary,
71
72 Quaternary,
77
78 Identical,
84}
85
86impl From<CollationStrength> for u32 {
87 fn from(strength: CollationStrength) -> Self {
88 match strength {
89 CollationStrength::Primary => 1,
90 CollationStrength::Secondary => 2,
91 CollationStrength::Tertiary => 3,
92 CollationStrength::Quaternary => 4,
93 CollationStrength::Identical => 5,
94 }
95 }
96}
97
98impl TryFrom<u32> for CollationStrength {
99 type Error = Error;
100
101 fn try_from(level: u32) -> Result<Self, Self::Error> {
102 Ok(match level {
103 1 => CollationStrength::Primary,
104 2 => CollationStrength::Secondary,
105 3 => CollationStrength::Tertiary,
106 4 => CollationStrength::Quaternary,
107 5 => CollationStrength::Identical,
108 _ => {
109 return Err(ErrorKind::InvalidArgument {
110 message: (format!("invalid collation strength: {}", level)),
111 }
112 .into())
113 }
114 })
115 }
116}
117
118impl Serialize for CollationStrength {
119 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
120 where
121 S: serde::Serializer,
122 {
123 let level = u32::from(*self);
124 serializer.serialize_i32(level.try_into().map_err(serde::ser::Error::custom)?)
125 }
126}
127
128impl<'de> Deserialize<'de> for CollationStrength {
129 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
130 where
131 D: serde::Deserializer<'de>,
132 {
133 let level = u32::deserialize(deserializer)?;
134 Self::try_from(level).map_err(serde::de::Error::custom)
135 }
136}
137
138impl std::fmt::Display for CollationStrength {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 std::fmt::Display::fmt(&u32::from(*self), f)
141 }
142}
143
144#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
147#[serde(rename_all = "kebab-case")]
148#[non_exhaustive]
149pub enum CollationCaseFirst {
150 Upper,
152
153 Lower,
155
156 Off,
159}
160
161impl std::str::FromStr for CollationCaseFirst {
162 type Err = Error;
163
164 fn from_str(s: &str) -> Result<Self, Self::Err> {
165 match s {
166 "upper" => Ok(CollationCaseFirst::Upper),
167 "lower" => Ok(CollationCaseFirst::Lower),
168 "off" => Ok(CollationCaseFirst::Off),
169 _ => Err(ErrorKind::InvalidArgument {
170 message: format!("invalid CollationCaseFirst: {}", s),
171 }
172 .into()),
173 }
174 }
175}
176
177impl CollationCaseFirst {
178 pub fn as_str(&self) -> &'static str {
180 match self {
181 CollationCaseFirst::Upper => "upper",
182 CollationCaseFirst::Lower => "lower",
183 CollationCaseFirst::Off => "off",
184 }
185 }
186}
187
188impl std::fmt::Display for CollationCaseFirst {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 std::fmt::Display::fmt(self.as_str(), f)
191 }
192}
193
194#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
197#[serde(rename_all = "kebab-case")]
198#[non_exhaustive]
199pub enum CollationAlternate {
200 NonIgnorable,
202
203 Shifted,
206}
207
208impl std::str::FromStr for CollationAlternate {
209 type Err = Error;
210
211 fn from_str(s: &str) -> Result<Self, Self::Err> {
212 match s {
213 "non-ignorable" => Ok(CollationAlternate::NonIgnorable),
214 "shifted" => Ok(CollationAlternate::Shifted),
215 _ => Err(ErrorKind::InvalidArgument {
216 message: format!("invalid collation alternate: {}", s),
217 }
218 .into()),
219 }
220 }
221}
222
223impl CollationAlternate {
224 pub fn as_str(&self) -> &'static str {
226 match self {
227 CollationAlternate::NonIgnorable => "non-ignorable",
228 CollationAlternate::Shifted => "shifted",
229 }
230 }
231}
232
233impl std::fmt::Display for CollationAlternate {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 std::fmt::Display::fmt(self.as_str(), f)
236 }
237}
238
239#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
241#[serde(rename_all = "kebab-case")]
242#[non_exhaustive]
243pub enum CollationMaxVariable {
244 Punct,
246
247 Space,
249}
250
251impl std::str::FromStr for CollationMaxVariable {
252 type Err = Error;
253
254 fn from_str(s: &str) -> Result<Self, Self::Err> {
255 match s {
256 "punct" => Ok(CollationMaxVariable::Punct),
257 "space" => Ok(CollationMaxVariable::Space),
258 _ => Err(ErrorKind::InvalidArgument {
259 message: format!("invalid collation max variable: {}", s),
260 }
261 .into()),
262 }
263 }
264}
265
266impl CollationMaxVariable {
267 pub fn as_str(&self) -> &'static str {
269 match self {
270 CollationMaxVariable::Punct => "punct",
271 CollationMaxVariable::Space => "space",
272 }
273 }
274}
275
276impl std::fmt::Display for CollationMaxVariable {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 std::fmt::Display::fmt(self.as_str(), f)
279 }
280}