1use 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#[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#[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#[derive(Clone, Debug)]
54#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
55pub enum IdentifierReference {
56 Identifier(Identifier),
57 QualifiedIdentifier(QualifiedIdentifier),
58}
59
60lazy_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
96impl 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 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 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 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
307impl 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 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 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 pub const fn module(&self) -> &Identifier {
435 &self.module
436 }
437 pub const fn member(&self) -> &Identifier {
438 &self.member
439 }
440 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
456impl 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 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 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 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#[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}