Skip to main content

json_ld/core/
lang_string.rs

1use json_syntax::JsonString;
2use langtag::{LangTag, LangTagBuf};
3
4use crate::{JsonLdDirection, JsonLdLenient};
5
6/// Language string.
7///
8/// A language string is a string tagged with language and reading direction information.
9///
10/// A valid language string is associated to either a language tag or a direction, or both.
11#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13pub struct JsonLdLangString {
14	/// Actual content of the string.
15	#[cfg_attr(feature = "serde", serde(rename = "@value"))]
16	data: JsonString,
17
18	#[cfg_attr(
19		feature = "serde",
20		serde(rename = "@language", skip_serializing_if = "Option::is_none")
21	)]
22	language: Option<JsonLdLenient<LangTagBuf>>,
23
24	#[cfg_attr(
25		feature = "serde",
26		serde(rename = "@direction", skip_serializing_if = "Option::is_none")
27	)]
28	direction: Option<JsonLdDirection>,
29}
30
31/// Raised when something tried to build a language string without language tag or direction.
32#[derive(Clone, Copy, Debug)]
33pub struct InvalidJsonLdLangString;
34
35impl JsonLdLangString {
36	/// Create a new language string.
37	pub fn new<T>(
38		data: T,
39		language: Option<JsonLdLenient<LangTagBuf>>,
40		direction: Option<JsonLdDirection>,
41	) -> Result<Self, T>
42	where
43		T: Into<JsonString>,
44	{
45		if language.is_some() || direction.is_some() {
46			Ok(Self {
47				data: data.into(),
48				language,
49				direction,
50			})
51		} else {
52			Err(data)
53		}
54	}
55
56	pub fn from_language(
57		data: impl Into<JsonString>,
58		language: impl Into<JsonLdLenient<LangTagBuf>>,
59	) -> Self {
60		Self {
61			data: data.into(),
62			language: Some(language.into()),
63			direction: None,
64		}
65	}
66
67	pub fn from_direction(data: impl Into<JsonString>, direction: JsonLdDirection) -> Self {
68		Self {
69			data: data.into(),
70			language: None,
71			direction: Some(direction),
72		}
73	}
74
75	pub fn into_parts(
76		self,
77	) -> (
78		JsonString,
79		Option<JsonLdLenient<LangTagBuf>>,
80		Option<JsonLdDirection>,
81	) {
82		(self.data, self.language, self.direction)
83	}
84
85	pub fn parts(
86		&self,
87	) -> (
88		&str,
89		Option<&JsonLdLenient<LangTagBuf>>,
90		Option<&JsonLdDirection>,
91	) {
92		(&self.data, self.language.as_ref(), self.direction.as_ref())
93	}
94
95	/// Reference to the underlying `str`.
96	#[inline(always)]
97	pub fn as_str(&self) -> &str {
98		self.data.as_ref()
99	}
100
101	/// Gets the associated language tag, if any.
102	#[inline(always)]
103	pub fn language(&self) -> Option<JsonLdLenient<&LangTag>> {
104		self.language.as_ref().map(|tag| tag.as_deref())
105	}
106
107	/// Sets the associated language tag.
108	///
109	/// If `None` is given, the direction must be set,
110	/// otherwise this function will fail with an [`InvalidJsonLdLangString`] error.
111	pub fn set_language(
112		&mut self,
113		language: Option<JsonLdLenient<LangTagBuf>>,
114	) -> Result<(), InvalidJsonLdLangString> {
115		if self.direction.is_some() || language.is_some() {
116			self.language = language;
117			Ok(())
118		} else {
119			Err(InvalidJsonLdLangString)
120		}
121	}
122
123	/// Gets the associated direction, if any.
124	#[inline(always)]
125	pub fn direction(&self) -> Option<JsonLdDirection> {
126		self.direction
127	}
128
129	/// Sets the associated direction.
130	///
131	/// If `None` is given, a language tag must be set,
132	/// otherwise this function will fail with an [`InvalidJsonLdLangString`] error.
133	pub fn set_direction(
134		&mut self,
135		direction: Option<JsonLdDirection>,
136	) -> Result<(), InvalidJsonLdLangString> {
137		if direction.is_some() || self.language.is_some() {
138			self.direction = direction;
139			Ok(())
140		} else {
141			Err(InvalidJsonLdLangString)
142		}
143	}
144
145	/// Set both the language tag and direction.
146	///
147	/// If both `language` and `direction` are `None`,
148	/// this function will fail with an [`InvalidJsonLdLangString`] error.
149	pub fn set(
150		&mut self,
151		language: Option<JsonLdLenient<LangTagBuf>>,
152		direction: Option<JsonLdDirection>,
153	) -> Result<(), InvalidJsonLdLangString> {
154		if direction.is_some() || language.is_some() {
155			self.language = language;
156			self.direction = direction;
157			Ok(())
158		} else {
159			Err(InvalidJsonLdLangString)
160		}
161	}
162
163	/// Returns a reference to this lang string as a [`JsonLdLangStr`].
164	pub fn as_lang_str(&self) -> JsonLdLangStr<'_> {
165		JsonLdLangStr {
166			data: &self.data,
167			language: self.language.as_ref().map(JsonLdLenient::as_deref),
168			direction: self.direction,
169		}
170	}
171}
172
173#[cfg(feature = "serde")]
174impl<'de> serde::Deserialize<'de> for JsonLdLangString {
175	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
176	where
177		D: serde::Deserializer<'de>,
178	{
179		#[derive(serde::Deserialize)]
180		struct Value {
181			#[serde(rename = "@value")]
182			data: JsonString,
183			#[serde(rename = "@language")]
184			language: Option<JsonLdLenient<LangTagBuf>>,
185			#[serde(rename = "@direction")]
186			direction: Option<JsonLdDirection>,
187		}
188
189		let value = Value::deserialize(deserializer)?;
190
191		Self::new(value.data, value.language, value.direction).map_err(serde::de::Error::custom)
192	}
193}
194
195/// Language string reference.
196///
197/// A language string is a string tagged with language and reading direction information.
198///
199/// A valid language string is associated to either a language tag or a direction, or both.
200#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize))]
202pub struct JsonLdLangStr<'a> {
203	/// Actual content of the string.
204	#[cfg_attr(feature = "serde", serde(rename = "@value"))]
205	data: &'a str,
206
207	#[cfg_attr(
208		feature = "serde",
209		serde(rename = "@language", skip_serializing_if = "Option::is_none")
210	)]
211	language: Option<JsonLdLenient<&'a LangTag>>,
212
213	#[cfg_attr(
214		feature = "serde",
215		serde(rename = "@direction", skip_serializing_if = "Option::is_none")
216	)]
217	direction: Option<JsonLdDirection>,
218}
219
220impl<'a> JsonLdLangStr<'a> {
221	/// Create a new language string reference.
222	pub fn new(
223		data: &'a str,
224		language: Option<JsonLdLenient<&'a LangTag>>,
225		direction: Option<JsonLdDirection>,
226	) -> Result<Self, InvalidJsonLdLangString> {
227		if language.is_some() || direction.is_some() {
228			Ok(Self {
229				data,
230				language,
231				direction,
232			})
233		} else {
234			Err(InvalidJsonLdLangString)
235		}
236	}
237
238	pub fn into_parts(
239		self,
240	) -> (
241		&'a str,
242		Option<JsonLdLenient<&'a LangTag>>,
243		Option<JsonLdDirection>,
244	) {
245		(self.data, self.language, self.direction)
246	}
247
248	/// Reference to the underlying `str`.
249	#[inline(always)]
250	pub fn as_str(&self) -> &'a str {
251		self.data
252	}
253
254	/// Gets the associated language tag, if any.
255	#[inline(always)]
256	pub fn language(&self) -> Option<JsonLdLenient<&'a LangTag>> {
257		self.language
258	}
259
260	/// Gets the associated direction, if any.
261	#[inline(always)]
262	pub fn direction(&self) -> Option<JsonLdDirection> {
263		self.direction
264	}
265}