sdml_core/model/constraints/
informal.rs1use crate::{
2 load::ModuleLoader,
3 model::{check::Validate, modules::Module, HasSourceSpan, References, Span},
4 store::ModuleStore,
5};
6use lazy_static::lazy_static;
7use regex::Regex;
8use sdml_errors::diagnostics::functions::invalid_language_tag;
9use std::{fmt::Display, str::FromStr};
10use tracing::warn;
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15#[derive(Clone, Debug)]
39#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
40pub struct ControlledLanguageString {
41 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
42 span: Option<Span>,
43 value: String,
45 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
46 language: Option<ControlledLanguageTag>,
47}
48
49#[derive(Clone, Debug)]
65#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
66pub struct ControlledLanguageTag {
67 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
68 span: Option<Span>,
69 value: String,
70}
71
72impl From<String> for ControlledLanguageString {
77 fn from(value: String) -> Self {
78 Self {
79 span: Default::default(),
80 value,
81 language: Default::default(),
82 }
83 }
84}
85
86impl HasSourceSpan for ControlledLanguageString {
87 fn with_source_span(self, span: Span) -> Self {
88 let mut self_mut = self;
89 self_mut.span = Some(span);
90 self_mut
91 }
92
93 fn source_span(&self) -> Option<&Span> {
94 self.span.as_ref()
95 }
96
97 fn set_source_span(&mut self, span: Span) {
98 self.span = Some(span);
99 }
100
101 fn unset_source_span(&mut self) {
102 self.span = None;
103 }
104}
105
106impl References for ControlledLanguageString {}
107
108impl Validate for ControlledLanguageString {
109 fn validate(
110 &self,
111 _top: &Module,
112 _cache: &impl ModuleStore,
113 _loader: &impl ModuleLoader,
114 _check_constraints: bool,
115 ) {
116 warn!("Missing Validation for ControlledLanguageString");
117 }
118}
119
120impl ControlledLanguageString {
121 pub fn new<S>(value: S, language: ControlledLanguageTag) -> Self
126 where
127 S: Into<String>,
128 {
129 Self {
130 span: Default::default(),
131 value: value.into(),
132 language: Some(language),
133 }
134 }
135
136 pub const fn value(&self) -> &String {
141 &self.value
142 }
143
144 pub fn set_value(&mut self, value: String) {
145 self.value = value;
146 }
147
148 pub const fn has_language(&self) -> bool {
151 self.language.is_some()
152 }
153
154 pub const fn language(&self) -> Option<&ControlledLanguageTag> {
155 self.language.as_ref()
156 }
157
158 pub fn set_language(&mut self, language: ControlledLanguageTag) {
159 self.language = Some(language);
160 }
161
162 pub fn unset_language(&mut self) {
163 self.language = None;
164 }
165}
166
167lazy_static! {
172 static ref LANGUAGE_TAG: Regex = Regex::new(r"^[a-z]{2,3}(-[A-Z][A-Za-z]{1,9})?$").unwrap();
173}
174
175impl Display for ControlledLanguageTag {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 write!(f, "@{}", self.value)
178 }
179}
180
181impl FromStr for ControlledLanguageTag {
182 type Err = crate::error::Error;
183
184 fn from_str(s: &str) -> Result<Self, Self::Err> {
185 if Self::is_valid_str(s) {
186 Ok(Self {
187 span: None,
188 value: s.to_string(),
189 })
190 } else {
191 Err(invalid_language_tag(0, None, s).into())
192 }
193 }
194}
195
196impl From<ControlledLanguageTag> for String {
197 fn from(value: ControlledLanguageTag) -> Self {
198 value.value
199 }
200}
201
202impl AsRef<str> for ControlledLanguageTag {
203 fn as_ref(&self) -> &str {
204 self.value.as_str()
205 }
206}
207
208impl PartialEq for ControlledLanguageTag {
209 fn eq(&self, other: &Self) -> bool {
210 self.value == other.value
211 }
212}
213
214impl PartialEq<str> for ControlledLanguageTag {
215 fn eq(&self, other: &str) -> bool {
216 self.value == other
217 }
218}
219
220impl Eq for ControlledLanguageTag {}
221
222impl HasSourceSpan for ControlledLanguageTag {
223 fn with_source_span(self, span: Span) -> Self {
224 let mut self_mut = self;
225 self_mut.span = Some(span);
226 self_mut
227 }
228
229 fn source_span(&self) -> Option<&Span> {
230 self.span.as_ref()
231 }
232
233 fn set_source_span(&mut self, span: Span) {
234 self.span = Some(span);
235 }
236
237 fn unset_source_span(&mut self) {
238 self.span = None;
239 }
240}
241
242impl Validate for ControlledLanguageTag {
243 fn validate(
244 &self,
245 _top: &Module,
246 _cache: &impl ModuleStore,
247 _loader: &impl ModuleLoader,
248 _check_constraints: bool,
249 ) {
250 assert!(Self::is_valid_str(&self.value))
251 }
252}
253
254impl ControlledLanguageTag {
255 pub fn new_unchecked(s: &str) -> Self {
260 Self {
261 span: None,
262 value: s.to_string(),
263 }
264 }
265
266 pub const fn value(&self) -> &String {
271 &self.value
272 }
273
274 pub fn set_value(&mut self, value: String) {
275 self.value = value;
276 }
277
278 pub fn is_valid_str(s: &str) -> bool {
283 LANGUAGE_TAG.is_match(s)
284 }
285}