opentalk_types_common/users/
language.rs1use icu_locid::{LanguageIdentifier, langid};
6
7use crate::utils::ExampleData;
8
9#[derive(
11 Default,
12 Debug,
13 Clone,
14 PartialEq,
15 Eq,
16 Hash,
17 derive_more::Display,
18 derive_more::FromStr,
19 derive_more::From,
20 derive_more::Into,
21 derive_more::AsRef,
22 derive_more::AsMut,
23)]
24#[cfg_attr(
25 feature = "serde",
26 derive(serde::Serialize, serde_with::DeserializeFromStr)
27)]
28pub struct Language(pub LanguageIdentifier);
29
30#[cfg(feature = "bincode")]
31mod bincode_impls {
32 use bincode::{
33 BorrowDecode, Decode, Encode,
34 de::{BorrowDecoder, Decoder},
35 enc::Encoder,
36 error::{DecodeError, EncodeError},
37 };
38
39 use super::Language;
40
41 impl Encode for Language {
42 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
43 Encode::encode(&self.0.to_string(), encoder)
44 }
45 }
46
47 impl<Context> Decode<Context> for Language {
48 fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
49 let decoded: String = Decode::<Context>::decode(decoder)?;
50 let language_identifier = decoded.parse().map_err(|e| {
51 DecodeError::OtherString(format!("Invalid language identifier: {e}"))
52 })?;
53 Ok(Self(language_identifier))
54 }
55 }
56
57 impl<'de, Context> BorrowDecode<'de, Context> for Language {
58 fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(
59 decoder: &mut D,
60 ) -> Result<Self, DecodeError> {
61 let decoded: String = Decode::<Context>::decode(decoder)?;
62 let language_identifier = decoded.parse().map_err(|e| {
63 DecodeError::OtherString(format!("Invalid language identifier: {e}"))
64 })?;
65 Ok(Self(language_identifier))
66 }
67 }
68}
69
70impl Language {
71 pub fn new() -> Self {
73 Self::default()
74 }
75}
76
77#[cfg(feature = "utoipa")]
78mod impl_to_schema {
79 use serde_json::json;
85 use utoipa::{
86 PartialSchema, ToSchema,
87 openapi::{ObjectBuilder, RefOr, Schema, Type},
88 };
89
90 use super::Language;
91 use crate::utils::ExampleData as _;
92
93 impl PartialSchema for Language {
94 fn schema() -> RefOr<Schema> {
95 ObjectBuilder::new()
96 .schema_type(Type::String)
97 .description(Some("A language identifier"))
98 .format(Some(utoipa::openapi::SchemaFormat::Custom(
99 "bcp-47".to_string(),
100 )))
101 .examples([json!(Language::example_data())])
102 .into()
103 }
104 }
105
106 impl ToSchema for Language {
107 fn schemas(schemas: &mut Vec<(String, RefOr<Schema>)>) {
108 schemas.push((Self::name().into(), Self::schema()));
109 }
110 }
111}
112
113impl ExampleData for Language {
114 fn example_data() -> Self {
115 Self(langid!("de"))
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use icu_locid::langid;
122 use pretty_assertions::assert_eq;
123
124 use super::Language;
125
126 #[test]
127 fn parse() {
128 assert_eq!(
129 "de-AT".parse::<Language>().unwrap(),
130 Language(langid!("de-AT"))
131 );
132 assert_eq!("xx".parse::<Language>().unwrap(), Language(langid!("xx")));
133 }
134
135 #[test]
136 fn parse_invalid() {
137 assert!("".parse::<Language>().is_err());
138 assert!("🚀".parse::<Language>().is_err());
139 }
140}