1use 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#[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#[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#[derive(Clone, Debug)]
58#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
59pub enum IdentifierReference {
60 Identifier(Identifier),
61 QualifiedIdentifier(QualifiedIdentifier),
62}
63
64lazy_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
73impl 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 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 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 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
292impl 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 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 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 pub const fn module(&self) -> &Identifier {
424 &self.module
425 }
426 pub const fn member(&self) -> &Identifier {
427 &self.member
428 }
429 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
445impl 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 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 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 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#[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}