sdml_core/model/
identifiers.rs

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