sdml_core/model/
identifiers.rs

1/*!
2Provide the Rust types that implement *identifier*-related components of the SDML Grammar.
3*/
4use crate::load::ModuleLoader;
5use crate::model::modules::Module;
6use crate::model::{HasSourceSpan, Span};
7use convert_case::{Case, Casing};
8use lazy_static::lazy_static;
9use regex::Regex;
10use sdml_errors::diagnostics::functions::{
11    identifier_not_preferred_case, invalid_identifier, IdentifierCaseConvention,
12};
13use std::{
14    fmt::{Debug, Display},
15    hash::Hash,
16    str::FromStr,
17};
18use tracing::error;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23// ------------------------------------------------------------------------------------------------
24// Public Types
25// ------------------------------------------------------------------------------------------------
26
27///
28/// Corresponds the grammar rule `identifier`.
29///
30#[derive(Clone, Debug)]
31#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
32pub struct Identifier {
33    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
34    span: Option<Span>,
35    value: String,
36}
37
38///
39/// Corresponds the grammar rule `qualified_identifier`.
40///
41#[derive(Clone, Debug)]
42#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
43pub struct QualifiedIdentifier {
44    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
45    span: Option<Span>,
46    module: Identifier,
47    member: Identifier,
48}
49
50///
51/// Corresponds the grammar rule `identifier_reference`.
52///
53#[derive(Clone, Debug)]
54#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
55pub enum IdentifierReference {
56    Identifier(Identifier),
57    QualifiedIdentifier(QualifiedIdentifier),
58}
59
60// ------------------------------------------------------------------------------------------------
61// Implementations
62// ------------------------------------------------------------------------------------------------
63
64lazy_static! {
65    static ref IDENTIFIER: Regex =
66        Regex::new(r"^[\p{Lu}\p{Ll}][\p{Lu}\p{Ll}\p{Nd}]*(?:_+[\p{Lu}\p{Ll}\p{Nd}]+)*$").unwrap();
67}
68
69const RESERVED_KEYWORDS: [&str; 19] = [
70    "as",
71    "base",
72    "datatype",
73    "end",
74    "entity",
75    "enum",
76    "event",
77    "group",
78    "identity",
79    "import",
80    "is",
81    "module",
82    "of",
83    "property",
84    "ref",
85    "source",
86    "structure",
87    "union",
88    "unknown",
89];
90const RESERVED_TYPES: [&str; 6] = ["string", "double", "decimal", "integer", "boolean", "iri"];
91const RESERVED_MODULES: [&str; 12] = [
92    "dc", "dc_am", "dc_terms", "dc_types", "iso_3166", "iso_4217", "owl", "rdf", "rdfs", "sdml",
93    "skos", "xsd",
94];
95
96// ------------------------------------------------------------------------------------------------
97// Implementations ❱ Identifier
98// ------------------------------------------------------------------------------------------------
99
100impl From<&Identifier> for String {
101    fn from(value: &Identifier) -> Self {
102        value.value.clone()
103    }
104}
105
106impl From<Identifier> for String {
107    fn from(value: Identifier) -> Self {
108        value.value
109    }
110}
111
112impl FromStr for Identifier {
113    type Err = crate::error::Error;
114
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        if Self::is_valid(s) {
117            Ok(Self {
118                span: None,
119                value: s.to_string(),
120            })
121        } else {
122            error!("Identifier::from_str({s}) is invalid");
123            Err(invalid_identifier(0, None, s).into())
124        }
125    }
126}
127
128impl AsRef<str> for Identifier {
129    fn as_ref(&self) -> &str {
130        self.value.as_str()
131    }
132}
133
134impl PartialEq<str> for Identifier {
135    fn eq(&self, other: &str) -> bool {
136        self.value.as_str() == other
137    }
138}
139
140impl PartialEq for Identifier {
141    fn eq(&self, other: &Self) -> bool {
142        self.value == other.value
143    }
144}
145
146impl Eq for Identifier {}
147
148impl PartialOrd for Identifier {
149    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
150        Some(self.cmp(other))
151    }
152}
153
154impl Ord for Identifier {
155    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
156        self.as_ref().cmp(other.as_ref())
157    }
158}
159
160impl Hash for Identifier {
161    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
162        // ignore: self.span.hash(state);
163        self.value.hash(state);
164    }
165}
166
167impl Display for Identifier {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        write!(f, "{}", self.value)
170    }
171}
172
173impl HasSourceSpan for Identifier {
174    fn with_source_span(self, span: Span) -> Self {
175        let mut self_mut = self;
176        self_mut.span = Some(span);
177        self_mut
178    }
179
180    fn source_span(&self) -> Option<&Span> {
181        self.span.as_ref()
182    }
183
184    fn set_source_span(&mut self, span: Span) {
185        self.span = Some(span);
186    }
187
188    fn unset_source_span(&mut self) {
189        self.span = None;
190    }
191}
192
193impl Identifier {
194    // --------------------------------------------------------------------------------------------
195    // Constructors
196    // --------------------------------------------------------------------------------------------
197
198    pub fn new_unchecked(s: &str) -> Self {
199        Self {
200            span: None,
201            value: s.to_string(),
202        }
203    }
204
205    #[inline(always)]
206    pub fn with_module(&self, module: Identifier) -> QualifiedIdentifier {
207        QualifiedIdentifier::new(module, self.clone())
208    }
209
210    #[inline(always)]
211    pub fn with_member(&self, member: Identifier) -> QualifiedIdentifier {
212        QualifiedIdentifier::new(self.clone(), member)
213    }
214
215    // --------------------------------------------------------------------------------------------
216    // Helpers
217    // --------------------------------------------------------------------------------------------
218
219    pub fn validate(
220        &self,
221        top: &Module,
222        loader: &impl ModuleLoader,
223        as_case: Option<IdentifierCaseConvention>,
224    ) {
225        if !Self::is_valid(&self.value) {
226            loader
227                .report(&invalid_identifier(
228                    top.file_id().copied().unwrap_or_default(),
229                    self.span.clone().map(|s| s.into()),
230                    &self.value,
231                ))
232                .unwrap();
233        }
234        if let Some(case) = as_case {
235            if !case.is_valid(self) {
236                loader
237                    .report(&identifier_not_preferred_case(
238                        top.file_id().copied().unwrap_or_default(),
239                        self.source_span().map(|span| span.byte_range()),
240                        self,
241                        case,
242                    ))
243                    .unwrap();
244            }
245        }
246    }
247
248    #[inline(always)]
249    pub fn is_valid<S>(s: S) -> bool
250    where
251        S: AsRef<str>,
252    {
253        let s = s.as_ref();
254        IDENTIFIER.is_match(s) && !Self::is_keyword(s)
255    }
256
257    #[inline(always)]
258    pub fn is_keyword<S>(s: S) -> bool
259    where
260        S: AsRef<str>,
261    {
262        RESERVED_KEYWORDS.contains(&s.as_ref())
263    }
264
265    #[inline(always)]
266    pub fn is_type_name<S>(s: S) -> bool
267    where
268        S: AsRef<str>,
269    {
270        RESERVED_TYPES.contains(&s.as_ref())
271    }
272
273    #[inline(always)]
274    pub fn is_library_module_name<S>(s: S) -> bool
275    where
276        S: AsRef<str>,
277    {
278        RESERVED_MODULES.contains(&s.as_ref())
279    }
280
281    #[inline(always)]
282    pub fn eq_with_span(&self, other: &Self) -> bool {
283        self.span == other.span && self.value == other.value
284    }
285
286    #[inline(always)]
287    pub fn to_type_label(&self) -> String {
288        self.value.to_case(Case::Title)
289    }
290
291    #[inline(always)]
292    pub fn to_variant_label(&self) -> String {
293        self.to_type_label()
294    }
295
296    #[inline(always)]
297    pub fn to_member_label(&self) -> String {
298        self.value.to_case(Case::Lower)
299    }
300
301    #[inline(always)]
302    pub fn to_module_label(&self) -> String {
303        self.to_member_label()
304    }
305}
306
307// ------------------------------------------------------------------------------------------------
308// Implementations ❱ QualifiedIdentifier
309// ------------------------------------------------------------------------------------------------
310
311impl From<QualifiedIdentifier> for String {
312    fn from(value: QualifiedIdentifier) -> Self {
313        String::from(&value)
314    }
315}
316
317impl From<&QualifiedIdentifier> for String {
318    fn from(value: &QualifiedIdentifier) -> Self {
319        value.to_string()
320    }
321}
322
323impl From<(Identifier, Identifier)> for QualifiedIdentifier {
324    fn from(value: (Identifier, Identifier)) -> Self {
325        Self::new(value.0, value.1)
326    }
327}
328
329impl FromStr for QualifiedIdentifier {
330    type Err = crate::error::Error;
331
332    fn from_str(s: &str) -> Result<Self, Self::Err> {
333        let parts = s.split(Self::SEPARATOR_STR).collect::<Vec<&str>>();
334        if parts.len() == 2 {
335            Ok(Self::new(
336                Identifier::from_str(parts[0])?,
337                Identifier::from_str(parts[1])?,
338            ))
339        } else {
340            error!("QualifiedIdentifier::from_str({s:?}) is invalid");
341            Err(invalid_identifier(0, None, s).into())
342        }
343    }
344}
345
346impl PartialEq<str> for QualifiedIdentifier {
347    fn eq(&self, other: &str) -> bool {
348        self.to_string().as_str() == other
349    }
350}
351
352impl PartialEq for QualifiedIdentifier {
353    fn eq(&self, other: &Self) -> bool {
354        self.module == other.module && self.member == other.member
355    }
356}
357
358impl Eq for QualifiedIdentifier {}
359
360impl Hash for QualifiedIdentifier {
361    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
362        // ignore: self.span.hash(state);
363        self.module.hash(state);
364        self.member.hash(state);
365    }
366}
367
368impl PartialOrd for QualifiedIdentifier {
369    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
370        Some(self.cmp(other))
371    }
372}
373
374impl Ord for QualifiedIdentifier {
375    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
376        match self.module.as_ref().cmp(other.module.as_ref()) {
377            core::cmp::Ordering::Equal => {}
378            ord => return ord,
379        }
380        self.member.as_ref().cmp(other.member.as_ref())
381    }
382}
383
384impl Display for QualifiedIdentifier {
385    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
386        write!(f, "{}:{}", self.module, self.member)
387    }
388}
389
390impl HasSourceSpan for QualifiedIdentifier {
391    fn with_source_span(self, span: Span) -> Self {
392        let mut self_mut = self;
393        self_mut.span = Some(span);
394        self_mut
395    }
396    fn source_span(&self) -> Option<&Span> {
397        self.span.as_ref()
398    }
399    fn set_source_span(&mut self, span: Span) {
400        self.span = Some(span);
401    }
402    fn unset_source_span(&mut self) {
403        self.span = None;
404    }
405}
406
407impl QualifiedIdentifier {
408    const SEPARATOR_STR: &'static str = ":";
409
410    // --------------------------------------------------------------------------------------------
411    // Constructors
412    // --------------------------------------------------------------------------------------------
413
414    pub fn new_unchecked(module: &str, member: &str) -> Self {
415        Self {
416            span: None,
417            module: Identifier::new_unchecked(module),
418            member: Identifier::new_unchecked(member),
419        }
420    }
421
422    pub const fn new(module: Identifier, member: Identifier) -> Self {
423        Self {
424            span: None,
425            module,
426            member,
427        }
428    }
429
430    // --------------------------------------------------------------------------------------------
431    // Fields
432    // --------------------------------------------------------------------------------------------
433
434    pub const fn module(&self) -> &Identifier {
435        &self.module
436    }
437    pub const fn member(&self) -> &Identifier {
438        &self.member
439    }
440    // --------------------------------------------------------------------------------------------
441    // Helpers
442    // --------------------------------------------------------------------------------------------
443
444    pub fn validate(&self, top: &Module, loader: &impl ModuleLoader) {
445        self.module
446            .validate(top, loader, Some(IdentifierCaseConvention::Module));
447        self.member
448            .validate(top, loader, Some(IdentifierCaseConvention::ImportedMember));
449    }
450
451    pub fn eq_with_span(&self, other: &Self) -> bool {
452        self.span == other.span && self.module == other.module && self.member == other.member
453    }
454}
455
456// ------------------------------------------------------------------------------------------------
457// Implementations ❱ IdentifierReference
458// ------------------------------------------------------------------------------------------------
459
460impl From<&Identifier> for IdentifierReference {
461    fn from(v: &Identifier) -> Self {
462        Self::Identifier(v.clone())
463    }
464}
465
466impl From<Identifier> for IdentifierReference {
467    fn from(v: Identifier) -> Self {
468        Self::Identifier(v)
469    }
470}
471
472impl From<&QualifiedIdentifier> for IdentifierReference {
473    fn from(v: &QualifiedIdentifier) -> Self {
474        Self::QualifiedIdentifier(v.clone())
475    }
476}
477
478impl From<QualifiedIdentifier> for IdentifierReference {
479    fn from(v: QualifiedIdentifier) -> Self {
480        Self::QualifiedIdentifier(v)
481    }
482}
483
484impl From<IdentifierReference> for String {
485    fn from(value: IdentifierReference) -> Self {
486        String::from(&value)
487    }
488}
489
490impl From<&IdentifierReference> for String {
491    fn from(value: &IdentifierReference) -> Self {
492        match value {
493            IdentifierReference::Identifier(v) => v.to_string(),
494            IdentifierReference::QualifiedIdentifier(v) => v.to_string(),
495        }
496    }
497}
498
499impl FromStr for IdentifierReference {
500    type Err = crate::error::Error;
501
502    fn from_str(s: &str) -> Result<Self, Self::Err> {
503        let parts = s
504            .split(QualifiedIdentifier::SEPARATOR_STR)
505            .collect::<Vec<&str>>();
506        if parts.len() == 1 {
507            Ok(Self::Identifier(Identifier::from_str(parts[0])?))
508        } else if parts.len() == 2 {
509            Ok(Self::QualifiedIdentifier(QualifiedIdentifier::new(
510                Identifier::from_str(parts[0])?,
511                Identifier::from_str(parts[1])?,
512            )))
513        } else {
514            error!("QualifiedIdentifier::from_str({s:?}) is invalid");
515            Err(invalid_identifier(0, None, s).into())
516        }
517    }
518}
519
520impl PartialEq<str> for IdentifierReference {
521    fn eq(&self, other: &str) -> bool {
522        self.to_string().as_str() == other
523    }
524}
525
526impl PartialEq for IdentifierReference {
527    fn eq(&self, other: &Self) -> bool {
528        match (self, other) {
529            (Self::Identifier(l0), Self::Identifier(r0)) => l0.eq(r0),
530            (Self::QualifiedIdentifier(l0), Self::QualifiedIdentifier(r0)) => l0.eq(r0),
531            _ => false,
532        }
533    }
534}
535
536impl Eq for IdentifierReference {}
537
538impl Hash for IdentifierReference {
539    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
540        core::mem::discriminant(self).hash(state);
541    }
542}
543
544impl PartialOrd for IdentifierReference {
545    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
546        Some(self.cmp(other))
547    }
548}
549
550impl Ord for IdentifierReference {
551    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
552        self.to_string().cmp(&other.to_string())
553    }
554}
555
556impl std::fmt::Display for IdentifierReference {
557    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
558        write!(
559            f,
560            "{}",
561            match self {
562                Self::Identifier(v) => v.to_string(),
563                Self::QualifiedIdentifier(v) => v.to_string(),
564            }
565        )
566    }
567}
568
569impl HasSourceSpan for IdentifierReference {
570    #[inline]
571    fn with_source_span(self, span: Span) -> Self {
572        match self {
573            Self::Identifier(v) => Self::Identifier(v.with_source_span(span)),
574            Self::QualifiedIdentifier(v) => Self::QualifiedIdentifier(v.with_source_span(span)),
575        }
576    }
577    #[inline]
578    fn source_span(&self) -> Option<&Span> {
579        match self {
580            Self::Identifier(v) => v.source_span(),
581            Self::QualifiedIdentifier(v) => v.source_span(),
582        }
583    }
584    #[inline]
585    fn set_source_span(&mut self, span: Span) {
586        match self {
587            Self::Identifier(v) => v.set_source_span(span),
588            Self::QualifiedIdentifier(v) => v.set_source_span(span),
589        }
590    }
591    #[inline]
592    fn unset_source_span(&mut self) {
593        match self {
594            Self::Identifier(v) => v.unset_source_span(),
595            Self::QualifiedIdentifier(v) => v.unset_source_span(),
596        }
597    }
598}
599
600impl IdentifierReference {
601    // --------------------------------------------------------------------------------------------
602    // Variants
603    // --------------------------------------------------------------------------------------------
604
605    pub const fn is_identifier(&self) -> bool {
606        match self {
607            Self::Identifier(_) => true,
608            _ => false,
609        }
610    }
611
612    pub const fn as_identifier(&self) -> Option<&Identifier> {
613        match self {
614            Self::Identifier(v) => Some(v),
615            _ => None,
616        }
617    }
618
619    pub const fn is_qualified_identifier(&self) -> bool {
620        match self {
621            Self::QualifiedIdentifier(_) => true,
622            _ => false,
623        }
624    }
625
626    pub const fn as_qualified_identifier(&self) -> Option<&QualifiedIdentifier> {
627        match self {
628            Self::QualifiedIdentifier(v) => Some(v),
629            _ => None,
630        }
631    }
632
633    // --------------------------------------------------------------------------------------------
634    // Fields
635    // --------------------------------------------------------------------------------------------
636
637    pub const fn module(&self) -> Option<&Identifier> {
638        match self {
639            IdentifierReference::Identifier(_) => None,
640            IdentifierReference::QualifiedIdentifier(v) => Some(v.module()),
641        }
642    }
643
644    pub const fn member(&self) -> &Identifier {
645        match self {
646            IdentifierReference::Identifier(v) => v,
647            IdentifierReference::QualifiedIdentifier(v) => v.member(),
648        }
649    }
650
651    // --------------------------------------------------------------------------------------------
652    // Helpers
653    // --------------------------------------------------------------------------------------------
654
655    pub fn validate(&self, top: &Module, loader: &impl ModuleLoader) {
656        match self {
657            IdentifierReference::Identifier(v) => v.validate(top, loader, None),
658            IdentifierReference::QualifiedIdentifier(v) => v.validate(top, loader),
659        };
660    }
661
662    pub fn eq_with_span(&self, other: &Self) -> bool {
663        match (self, other) {
664            (Self::Identifier(l0), Self::Identifier(r0)) => l0.eq_with_span(r0),
665            (Self::QualifiedIdentifier(l0), Self::QualifiedIdentifier(r0)) => l0.eq_with_span(r0),
666            _ => false,
667        }
668    }
669}
670
671// ------------------------------------------------------------------------------------------------
672// Modules
673// ------------------------------------------------------------------------------------------------
674
675#[cfg(test)]
676mod tests {
677    use super::Identifier;
678    use pretty_assertions::assert_eq;
679
680    #[test]
681    fn test_type_label() {
682        assert_eq!("Foo", Identifier::new_unchecked("Foo").to_type_label());
683        assert_eq!(
684            "Foo Bar",
685            Identifier::new_unchecked("FooBar").to_type_label()
686        );
687        assert_eq!(
688            "Foo Bar Baz",
689            Identifier::new_unchecked("FooBarBaz").to_type_label()
690        );
691        assert_eq!(
692            "Foo Bar Baz",
693            Identifier::new_unchecked("Foo_Bar_Baz").to_type_label()
694        );
695    }
696
697    #[test]
698    fn test_member_label() {
699        assert_eq!("foo", Identifier::new_unchecked("Foo").to_member_label());
700        assert_eq!(
701            "foo bar",
702            Identifier::new_unchecked("FooBar").to_member_label()
703        );
704        assert_eq!(
705            "foo bar baz",
706            Identifier::new_unchecked("FooBarBaz").to_member_label()
707        );
708        assert_eq!(
709            "foo bar baz",
710            Identifier::new_unchecked("Foo_Bar_Baz").to_member_label()
711        );
712    }
713}