sdml_core/model/constraints/
informal.rs

1use 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// ------------------------------------------------------------------------------------------------
16// Public Types ❱ Constraints ❱ Informal     ,
17// ------------------------------------------------------------------------------------------------
18
19///
20/// Corresponds to the grammar rule `informal_constraint`.
21///
22/// This structure captures an informal, or semi-formal constraint as a natural language string
23/// string.
24///
25/// 1. `"some cars have manual transmissions"` is an informal constraint in some unidentified
26///    natural language.
27/// 2. `"some cars have manual transmissions"@en` is an informal constraint in English.
28/// 3. `"there is a car that has a a:manual transmission."@en-ACE` is a semi-formal constraint in
29///    Attempto Controlled English (ACE).
30///
31/// We classify the last example as *semi-formal*, even though ACE is formally defined,
32/// because SDML does not expect (although does not prohibit) the translation from this form into
33/// the logical structure of a `ConstraintSentence`.
34///
35/// In the last example above the prefix `a:` on manual identifies the term *manual* it as an
36/// adjective applied to the word term *transmission*.
37///
38#[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    /// Corresponds to the grammar rule `quoted_string`.
44    value: String,
45    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
46    language: Option<ControlledLanguageTag>,
47}
48
49///
50/// Corresponds to the grammar rule `controlled_language_tag`.
51///
52/// 1. Required natural language identifier, either a 2 or 3 character
53///    code from ISO-639.
54/// 2. An optional identifier representing the controlled language scheme.
55///
56/// There is no registry for controlled language schemes, and SDML makes no requirement
57/// for the support of any particular scheme. The following are commonly used schemes
58/// and their identifiers:
59///
60/// - **CLCE**: Common Logic Controlled English (see [Sowa, 2004](http://www.jfsowa.com/clce/specs.htm)).
61/// - **ACE** or **Attempto**: Attempto Controlled English (ACE) (see
62///   [attempto.ifi.uzh.ch](http://attempto.ifi.uzh.ch/site/)).
63///
64#[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
72// ------------------------------------------------------------------------------------------------
73// Implementations ❱ Constraints ❱ ControlledLanguageString
74// ------------------------------------------------------------------------------------------------
75
76impl 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    // --------------------------------------------------------------------------------------------
122    // Constructors
123    // --------------------------------------------------------------------------------------------
124
125    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    // --------------------------------------------------------------------------------------------
137    // Fields
138    // --------------------------------------------------------------------------------------------
139
140    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    // --------------------------------------------------------------------------------------------
149
150    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
167// ------------------------------------------------------------------------------------------------
168// Implementations ❱ Constraints ❱ ControlledLanguageTag
169// ------------------------------------------------------------------------------------------------
170
171lazy_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    // --------------------------------------------------------------------------------------------
256    // Constructors
257    // --------------------------------------------------------------------------------------------
258
259    pub fn new_unchecked(s: &str) -> Self {
260        Self {
261            span: None,
262            value: s.to_string(),
263        }
264    }
265
266    // --------------------------------------------------------------------------------------------
267    // Fields
268    // --------------------------------------------------------------------------------------------
269
270    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    // --------------------------------------------------------------------------------------------
279    // Helpers
280    // --------------------------------------------------------------------------------------------
281
282    pub fn is_valid_str(s: &str) -> bool {
283        LANGUAGE_TAG.is_match(s)
284    }
285}