1use crate::{object::InvalidExpandedJson, Direction, LenientLangTag, LenientLangTagBuf};
2
3#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize))]
10pub struct LangString {
11 #[cfg_attr(feature = "serde", serde(rename = "@value"))]
13 data: json_ld_syntax::String,
14
15 #[cfg_attr(
16 feature = "serde",
17 serde(rename = "@language", skip_serializing_if = "Option::is_none")
18 )]
19 language: Option<LenientLangTagBuf>,
20
21 #[cfg_attr(
22 feature = "serde",
23 serde(rename = "@direction", skip_serializing_if = "Option::is_none")
24 )]
25 direction: Option<Direction>,
26}
27
28#[derive(Clone, Copy, Debug)]
30pub struct InvalidLangString;
31
32impl LangString {
33 pub fn new(
35 data: json_ld_syntax::String,
36 language: Option<LenientLangTagBuf>,
37 direction: Option<Direction>,
38 ) -> Result<Self, json_ld_syntax::String> {
39 if language.is_some() || direction.is_some() {
40 Ok(Self {
41 data,
42 language,
43 direction,
44 })
45 } else {
46 Err(data)
47 }
48 }
49
50 pub fn into_parts(
51 self,
52 ) -> (
53 json_ld_syntax::String,
54 Option<LenientLangTagBuf>,
55 Option<Direction>,
56 ) {
57 (self.data, self.language, self.direction)
58 }
59
60 pub fn parts(&self) -> (&str, Option<&LenientLangTagBuf>, Option<&Direction>) {
61 (&self.data, self.language.as_ref(), self.direction.as_ref())
62 }
63
64 #[inline(always)]
66 pub fn as_str(&self) -> &str {
67 self.data.as_ref()
68 }
69
70 #[inline(always)]
72 pub fn language(&self) -> Option<&LenientLangTag> {
73 self.language
74 .as_ref()
75 .map(|tag| tag.as_lenient_lang_tag_ref())
76 }
77
78 pub fn set_language(
83 &mut self,
84 language: Option<LenientLangTagBuf>,
85 ) -> Result<(), InvalidLangString> {
86 if self.direction.is_some() || language.is_some() {
87 self.language = language;
88 Ok(())
89 } else {
90 Err(InvalidLangString)
91 }
92 }
93
94 #[inline(always)]
96 pub fn direction(&self) -> Option<Direction> {
97 self.direction
98 }
99
100 pub fn set_direction(&mut self, direction: Option<Direction>) -> Result<(), InvalidLangString> {
105 if direction.is_some() || self.language.is_some() {
106 self.direction = direction;
107 Ok(())
108 } else {
109 Err(InvalidLangString)
110 }
111 }
112
113 pub fn set(
118 &mut self,
119 language: Option<LenientLangTagBuf>,
120 direction: Option<Direction>,
121 ) -> Result<(), InvalidLangString> {
122 if direction.is_some() || language.is_some() {
123 self.language = language;
124 self.direction = direction;
125 Ok(())
126 } else {
127 Err(InvalidLangString)
128 }
129 }
130
131 pub fn as_lang_str(&self) -> LangStr {
133 LangStr {
134 data: &self.data,
135 language: self.language.as_deref(),
136 direction: self.direction,
137 }
138 }
139
140 pub(crate) fn try_from_json(
141 object: json_syntax::Object,
142 value: json_syntax::Value,
143 language: Option<json_syntax::Value>,
144 direction: Option<json_syntax::Value>,
145 ) -> Result<Self, InvalidExpandedJson> {
146 let data = match value {
147 json_syntax::Value::String(s) => s,
148 v => {
149 return Err(InvalidExpandedJson::Unexpected(
150 v.kind(),
151 json_syntax::Kind::String,
152 ))
153 }
154 };
155
156 let language = match language {
157 Some(json_syntax::Value::String(value)) => {
158 let (tag, _) = LenientLangTagBuf::new(value.to_string());
159 Some(tag)
160 }
161 Some(v) => {
162 return Err(InvalidExpandedJson::Unexpected(
163 v.kind(),
164 json_syntax::Kind::String,
165 ))
166 }
167 None => None,
168 };
169
170 let direction = match direction {
171 Some(json_syntax::Value::String(value)) => match Direction::try_from(value.as_str()) {
172 Ok(direction) => Some(direction),
173 Err(_) => return Err(InvalidExpandedJson::InvalidDirection),
174 },
175 Some(v) => {
176 return Err(InvalidExpandedJson::Unexpected(
177 v.kind(),
178 json_syntax::Kind::String,
179 ))
180 }
181 None => None,
182 };
183
184 match object.into_iter().next() {
185 None => Ok(Self::new(data, language, direction).unwrap()),
186 Some(_) => Err(InvalidExpandedJson::UnexpectedEntry),
187 }
188 }
189}
190
191#[cfg(feature = "serde")]
192impl<'de> serde::Deserialize<'de> for LangString {
193 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
194 where
195 D: serde::Deserializer<'de>,
196 {
197 #[derive(serde::Deserialize)]
198 struct Value {
199 #[serde(rename = "@value")]
200 data: json_ld_syntax::String,
201 #[serde(rename = "@language")]
202 language: Option<LenientLangTagBuf>,
203 #[serde(rename = "@direction")]
204 direction: Option<Direction>,
205 }
206
207 let value = Value::deserialize(deserializer)?;
208
209 Self::new(value.data, value.language, value.direction).map_err(serde::de::Error::custom)
210 }
211}
212
213#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize))]
220pub struct LangStr<'a> {
221 #[cfg_attr(feature = "serde", serde(rename = "@value"))]
223 data: &'a str,
224
225 #[cfg_attr(
226 feature = "serde",
227 serde(rename = "@language", skip_serializing_if = "Option::is_none")
228 )]
229 language: Option<&'a LenientLangTag>,
230
231 #[cfg_attr(
232 feature = "serde",
233 serde(rename = "@direction", skip_serializing_if = "Option::is_none")
234 )]
235 direction: Option<Direction>,
236}
237
238impl<'a> LangStr<'a> {
239 pub fn new(
241 data: &'a str,
242 language: Option<&'a LenientLangTag>,
243 direction: Option<Direction>,
244 ) -> Result<Self, InvalidLangString> {
245 if language.is_some() || direction.is_some() {
246 Ok(Self {
247 data,
248 language,
249 direction,
250 })
251 } else {
252 Err(InvalidLangString)
253 }
254 }
255
256 pub fn into_parts(self) -> (&'a str, Option<&'a LenientLangTag>, Option<Direction>) {
257 (self.data, self.language, self.direction)
258 }
259
260 #[inline(always)]
262 pub fn as_str(&self) -> &'a str {
263 self.data
264 }
265
266 #[inline(always)]
268 pub fn language(&self) -> Option<&'a LenientLangTag> {
269 self.language
270 }
271
272 #[inline(always)]
274 pub fn direction(&self) -> Option<Direction> {
275 self.direction
276 }
277}