···1+use serde::{Deserialize, Deserializer, Serialize, de::Error};
2+use smol_str::{SmolStr, ToSmolStr};
3+use std::fmt;
4+use std::{ops::Deref, str::FromStr};
5+6+use crate::CowStr;
7+8+/// A [Timestamp Identifier].
9+///
10+/// [Timestamp Identifier]: https://atproto.com/specs/lang
11+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Hash)]
12+#[serde(transparent)]
13+#[repr(transparent)]
14+pub struct Lang(SmolStr);
15+16+impl Lang {
17+ /// Parses an IETF language tag from the given string.
18+ pub fn new<T>(lang: &T) -> Result<Self, langtag::InvalidLangTag<&T>>
19+ where
20+ T: AsRef<str> + ?Sized,
21+ {
22+ let tag = langtag::LangTag::new(lang)?;
23+ Ok(Lang(SmolStr::new_inline(tag.as_str())))
24+ }
25+26+ /// Infallible constructor for when you *know* the string is a valid IETF language tag.
27+ /// Will panic on invalid tag. If you're manually decoding atproto records
28+ /// or API values you know are valid (rather than using serde), this is the one to use.
29+ /// The From<String> and From<CowStr> impls use the same logic.
30+ pub fn raw(lang: impl AsRef<str>) -> Self {
31+ let lang = lang.as_ref();
32+ let tag = langtag::LangTag::new(lang).expect("valid IETF language tag");
33+ Lang(SmolStr::new_inline(tag.as_str()))
34+ }
35+36+ /// Infallible constructor for when you *know* the string is a valid IETF language tag.
37+ /// Marked unsafe because responsibility for upholding the invariant is on the developer.
38+ pub unsafe fn unchecked(lang: impl AsRef<str>) -> Self {
39+ let lang = lang.as_ref();
40+ Self(SmolStr::new_inline(lang))
41+ }
42+43+ /// Returns the LANG as a string slice.
44+ pub fn as_str(&self) -> &str {
45+ {
46+ let this = &self.0;
47+ this
48+ }
49+ }
50+}
51+52+impl FromStr for Lang {
53+ type Err = SmolStr;
54+55+ fn from_str(s: &str) -> Result<Self, Self::Err> {
56+ Self::new(s).map_err(|e| e.0.to_smolstr())
57+ }
58+}
59+60+impl<'de> Deserialize<'de> for Lang {
61+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62+ where
63+ D: Deserializer<'de>,
64+ {
65+ let value: &str = Deserialize::deserialize(deserializer)?;
66+ Self::new(value).map_err(D::Error::custom)
67+ }
68+}
69+70+impl fmt::Display for Lang {
71+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72+ f.write_str(&self.0)
73+ }
74+}
75+76+impl From<Lang> for String {
77+ fn from(value: Lang) -> Self {
78+ value.0.to_string()
79+ }
80+}
81+82+impl From<Lang> for SmolStr {
83+ fn from(value: Lang) -> Self {
84+ value.0
85+ }
86+}
87+88+impl From<String> for Lang {
89+ fn from(value: String) -> Self {
90+ Self::raw(&value)
91+ }
92+}
93+94+impl<'t> From<CowStr<'t>> for Lang {
95+ fn from(value: CowStr<'t>) -> Self {
96+ Self::raw(&value)
97+ }
98+}
99+100+impl AsRef<str> for Lang {
101+ fn as_ref(&self) -> &str {
102+ self.as_str()
103+ }
104+}
105+106+impl Deref for Lang {
107+ type Target = str;
108+109+ fn deref(&self) -> &Self::Target {
110+ self.as_str()
111+ }
112+}