sdml_core/model/
modules.rs

1/*!
2Provide the Rust types that implement *module*-related components of the SDML Grammar.
3*/
4use crate::load::ModuleLoader;
5use crate::model::definitions::{
6    DatatypeDef, EntityDef, EnumDef, EventDef, PropertyDef, StructureDef, UnionDef,
7};
8use crate::model::References;
9use crate::model::{
10    annotations::{Annotation, HasAnnotations},
11    check::{MaybeIncomplete, Validate},
12    definitions::{Definition, RdfDef, TypeClassDef},
13    identifiers::{Identifier, IdentifierReference, QualifiedIdentifier},
14    HasBody, HasName, HasSourceSpan, Span,
15};
16use crate::store::{InMemoryModuleCache, ModuleStore};
17use sdml_errors::diagnostics::functions::{
18    definition_not_found, imported_module_not_found, library_definition_not_allowed,
19    module_is_incomplete, module_version_info_empty, module_version_mismatch,
20    module_version_not_found, IdentifierCaseConvention,
21};
22use sdml_errors::{Error, FileId};
23use std::collections::HashMap;
24use std::fmt::Display;
25use std::hash::Hash;
26use std::path::PathBuf;
27use std::{collections::HashSet, fmt::Debug};
28use url::Url;
29
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Serialize};
32
33// ------------------------------------------------------------------------------------------------
34// Public Types ❱ Modules
35// ------------------------------------------------------------------------------------------------
36
37///
38/// Corresponds the grammar rule `module`.
39///
40#[derive(Clone, Debug)]
41#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
42pub struct Module {
43    #[cfg_attr(feature = "serde", serde(skip))]
44    source_file: Option<PathBuf>,
45    #[cfg_attr(feature = "serde", serde(skip))]
46    file_id: Option<FileId>,
47    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
48    span: Option<Box<Span>>,
49    name: Identifier,
50    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
51    base_uri: Option<HeaderValue<Url>>,
52    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
53    version_info: Option<HeaderValue<String>>,
54    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
55    version_uri: Option<HeaderValue<Url>>,
56    body: ModuleBody,
57}
58
59#[derive(Clone, Debug)]
60#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
61pub struct HeaderValue<T> {
62    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
63    span: Option<Box<Span>>,
64    value: T,
65}
66
67///
68/// Corresponds the grammar rule `module_body`.
69///
70#[derive(Clone, Debug, Default)]
71#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
72pub struct ModuleBody {
73    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
74    span: Option<Box<Span>>,
75    file_id: Option<FileId>, // <- to report errors
76    is_library: bool,        // <- to catch errors
77    imports: Vec<ImportStatement>,
78    annotations: Vec<Annotation>,
79    definitions: Vec<Definition>,
80}
81
82// ------------------------------------------------------------------------------------------------
83// Public Types ❱ Modules ❱ Imports
84// ------------------------------------------------------------------------------------------------
85
86///
87/// Corresponds the grammar rule `import_statement`.
88///
89#[derive(Clone, Debug, Default)]
90#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
91pub struct ImportStatement {
92    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
93    span: Option<Box<Span>>,
94    imports: Vec<Import>,
95}
96
97///
98/// Corresponds the grammar rule `import`.
99///
100#[derive(Clone, Debug, PartialEq, Eq, Hash)]
101#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
102pub enum Import {
103    /// Corresponds to the grammar rule `module_import`.
104    Module(ModuleImport),
105    /// Corresponds to the grammar rule `member_import`.
106    Member(QualifiedIdentifier),
107}
108
109///
110/// Corresponds the grammar rule `module_import`.
111///
112#[derive(Clone, Debug)]
113#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
114pub struct ModuleImport {
115    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
116    span: Option<Box<Span>>,
117    name: Identifier,
118    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
119    version_uri: Option<HeaderValue<Url>>,
120}
121
122// ------------------------------------------------------------------------------------------------
123// Implementations ❱ Modules
124// ------------------------------------------------------------------------------------------------
125
126impl_has_source_span_for!(Module);
127
128impl_has_name_for!(Module);
129
130impl HasBody for Module {
131    type Body = ModuleBody;
132
133    fn body(&self) -> &Self::Body {
134        &self.body
135    }
136
137    fn body_mut(&mut self) -> &mut Self::Body {
138        &mut self.body
139    }
140
141    fn set_body(&mut self, body: Self::Body) {
142        let mut body_mut = body;
143        body_mut.file_id = self.file_id;
144        body_mut.set_library_status(self.name());
145        self.body = body_mut;
146    }
147}
148
149impl References for Module {
150    fn referenced_types<'a>(&'a self, names: &mut HashSet<&'a IdentifierReference>) {
151        self.body.referenced_types(names);
152    }
153
154    fn referenced_annotations<'a>(&'a self, names: &mut HashSet<&'a IdentifierReference>) {
155        self.body.referenced_annotations(names);
156    }
157}
158
159impl Module {
160    // --------------------------------------------------------------------------------------------
161    // Module :: Constructors
162    // --------------------------------------------------------------------------------------------
163
164    pub fn empty(name: Identifier) -> Self {
165        Self::new(name, ModuleBody::default())
166    }
167
168    pub fn new(name: Identifier, body: ModuleBody) -> Self {
169        let mut body = body;
170        body.set_library_status(&name);
171        Self {
172            source_file: None,
173            file_id: None,
174            span: None,
175            name,
176            base_uri: None,
177            version_info: None,
178            version_uri: None,
179            body,
180        }
181    }
182
183    // --------------------------------------------------------------------------------------------
184    // Module :: Fields
185    // --------------------------------------------------------------------------------------------
186
187    pub fn with_source_file(self, source_file: PathBuf) -> Self {
188        Self {
189            source_file: Some(source_file),
190            ..self
191        }
192    }
193
194    pub fn with_base_uri(self, base_uri: Url) -> Self {
195        Self {
196            base_uri: Some(base_uri.into()),
197            ..self
198        }
199    }
200
201    pub fn with_version_info<S>(self, version_info: S) -> Self
202    where
203        S: Into<String>,
204    {
205        Self {
206            version_info: Some(HeaderValue::from(version_info.into())),
207            ..self
208        }
209    }
210
211    pub fn with_version_uri(self, version_uri: Url) -> Self {
212        Self {
213            version_uri: Some(version_uri.into()),
214            ..self
215        }
216    }
217
218    get_and_set!(pub source_file, set_source_file, unset_source_file => optional has_source_file, PathBuf);
219
220    get_and_set!(pub base_uri, set_base_uri, unset_base_uri => optional has_base_uri, HeaderValue<Url>);
221
222    get_and_set!(pub version_info, set_version_info, unset_version_info => optional has_version_info, HeaderValue<String>);
223
224    get_and_set!(pub version_uri, set_version_uri, unset_version_uri => optional has_version_uri, HeaderValue<Url>);
225
226    get_and_set!(pub file_id, set_file_id, unset_file_id => optional has_file_id, FileId);
227
228    // --------------------------------------------------------------------------------------------
229
230    delegate!(pub imported_modules, HashSet<&Identifier>, body);
231
232    delegate!(pub imported_module_versions, HashMap<&Identifier, Option<&HeaderValue<Url>>>, body);
233
234    delegate!(pub imported_types, HashSet<&QualifiedIdentifier>, body);
235
236    delegate!(pub defined_names, HashSet<&Identifier>, body);
237
238    // --------------------------------------------------------------------------------------------
239    // Module :: Pseudo-Validate
240    // --------------------------------------------------------------------------------------------
241
242    pub fn is_incomplete(&self, cache: &InMemoryModuleCache) -> bool {
243        if !self.is_library_module() {
244            self.body.is_incomplete(self, cache)
245        } else {
246            false
247        }
248    }
249
250    ///
251    /// # Checks
252    ///
253    /// 1. name is valid [`Identifier`]
254    /// 1. base URI is absolute [`Url`]
255    /// 1. version info string is not empty (warning)
256    /// 1. version URI is absolute [`Url`]
257    /// 1. body is valid
258    ///
259    pub fn validate(
260        &self,
261        cache: &InMemoryModuleCache,
262        loader: &impl ModuleLoader,
263        check_constraints: bool,
264    ) {
265        if !self.is_library_module() {
266            self.name
267                .validate(self, loader, Some(IdentifierCaseConvention::Module));
268            if let Some(version_info) = self.version_info() {
269                if version_info.as_ref().is_empty() {
270                    loader
271                        .report(&module_version_info_empty(
272                            self.file_id().copied().unwrap_or_default(),
273                            version_info.source_span().map(|span| span.byte_range()),
274                        ))
275                        .unwrap();
276                }
277            }
278            self.body.validate(self, cache, loader, check_constraints);
279            if self.is_incomplete(cache) {
280                loader
281                    .report(&module_is_incomplete(
282                        self.file_id().copied().unwrap_or_default(),
283                        self.source_span().map(|span| span.byte_range()),
284                        self.name(),
285                    ))
286                    .unwrap()
287            }
288        }
289    }
290
291    // --------------------------------------------------------------------------------------------
292    // Module :: Helpers
293    // --------------------------------------------------------------------------------------------
294
295    pub fn is_library_module(&self) -> bool {
296        Identifier::is_library_module_name(self.name().as_ref())
297    }
298
299    pub fn resolve_local(&self, name: &Identifier) -> Option<&Definition> {
300        self.body().definitions().find(|def| def.name() == name)
301    }
302}
303
304// ------------------------------------------------------------------------------------------------
305
306impl<T> AsRef<T> for HeaderValue<T> {
307    fn as_ref(&self) -> &T {
308        &self.value
309    }
310}
311
312impl<T> From<T> for HeaderValue<T> {
313    fn from(value: T) -> Self {
314        Self { span: None, value }
315    }
316}
317
318impl<T: Display> Display for HeaderValue<T> {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        write!(f, "{}", self.value)
321    }
322}
323
324impl<T: PartialEq> PartialEq for HeaderValue<T> {
325    fn eq(&self, other: &Self) -> bool {
326        self.value == other.value
327    }
328}
329
330impl<T: PartialEq> PartialEq<T> for HeaderValue<T> {
331    fn eq(&self, other: &T) -> bool {
332        self.value == *other
333    }
334}
335
336impl<T: Eq> Eq for HeaderValue<T> {}
337
338impl<T: Hash> Hash for HeaderValue<T> {
339    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
340        // ignore: self.span.hash(state);
341        self.value.hash(state);
342    }
343}
344
345impl<T> HasSourceSpan for HeaderValue<T> {
346    fn with_source_span(self, span: Span) -> Self {
347        Self {
348            span: Some(Box::new(span)),
349            ..self
350        }
351    }
352
353    fn source_span(&self) -> Option<&Span> {
354        match &self.span {
355            Some(v) => Some(v.as_ref()),
356            None => None,
357        }
358    }
359
360    fn set_source_span(&mut self, span: Span) {
361        self.span = Some(Box::new(span));
362    }
363
364    fn unset_source_span(&mut self) {
365        self.span = None;
366    }
367}
368
369impl<T: PartialEq> HeaderValue<T> {
370    pub fn eq_with_span(&self, other: &Self) -> bool {
371        self.span == other.span && self.value == other.value
372    }
373}
374
375impl<T> HeaderValue<T> {
376    get_and_set!(pub value, set_value => T);
377}
378
379// ------------------------------------------------------------------------------------------------
380
381impl_has_source_span_for!(ModuleBody);
382
383impl_has_annotations_for!(ModuleBody);
384
385impl References for ModuleBody {
386    fn referenced_types<'a>(&'a self, names: &mut HashSet<&'a IdentifierReference>) {
387        self.definitions
388            .iter()
389            .for_each(|def| def.referenced_types(names))
390    }
391
392    fn referenced_annotations<'a>(&'a self, names: &mut HashSet<&'a IdentifierReference>) {
393        self.definitions
394            .iter()
395            .for_each(|def| def.referenced_annotations(names));
396    }
397}
398
399impl_maybe_incomplete_for!(ModuleBody; over definitions);
400
401impl Validate for ModuleBody {
402    ///
403    /// # Checks
404    ///
405    /// 1. All import statements are valid [`ImportStatement`]s
406    /// 1. All annotations are valid [`Annotation`]s
407    /// 1. All definitions are valid [`Definition`]s
408    ///
409    fn validate(
410        &self,
411        top: &Module,
412        cache: &impl ModuleStore,
413        loader: &impl ModuleLoader,
414        check_constraints: bool,
415    ) {
416        self.imports()
417            .for_each(|imp| imp.validate(top, cache, loader, check_constraints));
418        self.annotations()
419            .for_each(|ann| ann.validate(top, cache, loader, check_constraints));
420        self.definitions()
421            .for_each(|def| def.validate(top, cache, loader, check_constraints));
422    }
423}
424
425impl ModuleBody {
426    pub fn set_library_status(&mut self, module_name: &Identifier) {
427        self.is_library = Identifier::is_library_module_name(module_name);
428    }
429
430    // --------------------------------------------------------------------------------------------
431    // ModuleBody :: Fields
432    // --------------------------------------------------------------------------------------------
433
434    get_and_set_vec!(
435        pub
436        has has_imports,
437        imports_len,
438        imports,
439        imports_mut,
440        add_to_imports,
441        extend_imports
442            => imports, ImportStatement
443    );
444
445    pub fn has_definitions(&self) -> bool {
446        !self.definitions.is_empty()
447    }
448
449    pub fn definitions_len(&self) -> usize {
450        self.definitions.len()
451    }
452
453    pub fn definitions(&self) -> impl Iterator<Item = &Definition> {
454        self.definitions.iter()
455    }
456
457    pub fn definitions_mut(&mut self) -> impl Iterator<Item = &mut Definition> {
458        self.definitions.iter_mut()
459    }
460
461    pub fn add_to_definitions<I>(&mut self, value: I) -> Result<(), Error>
462    where
463        I: Into<Definition>,
464    {
465        let definition = value.into();
466        if !self.is_library && matches!(definition, Definition::Rdf(_) | Definition::TypeClass(_)) {
467            Err(library_definition_not_allowed(
468                self.file_id.unwrap_or_default(),
469                definition.source_span().map(|s| s.into()),
470                definition.name(),
471            )
472            .into())
473        } else {
474            self.definitions.push(definition);
475            Ok(())
476        }
477    }
478
479    pub fn extend_definitions<I>(&mut self, extension: I) -> Result<(), Error>
480    where
481        I: IntoIterator<Item = Definition>,
482    {
483        // we do this manually to ensure the library rules in ModuleBody::push
484        for definition in extension.into_iter() {
485            self.add_to_definitions(definition)?;
486        }
487        Ok(())
488    }
489
490    // --------------------------------------------------------------------------------------------
491    // ModuleBody :: Helpers
492    // --------------------------------------------------------------------------------------------
493
494    pub fn imported_modules(&self) -> HashSet<&Identifier> {
495        self.imports()
496            .flat_map(|stmt| stmt.imported_modules())
497            .collect()
498    }
499
500    pub fn imported_module_versions(&self) -> HashMap<&Identifier, Option<&HeaderValue<Url>>> {
501        self.imports()
502            .flat_map(|stmt| stmt.imported_module_versions())
503            .collect()
504    }
505
506    pub fn imported_types(&self) -> HashSet<&QualifiedIdentifier> {
507        self.imports()
508            .flat_map(|stmt| stmt.imported_types())
509            .collect()
510    }
511
512    // --------------------------------------------------------------------------------------------
513
514    #[inline]
515    pub fn get_definition(&self, name: &Identifier) -> Option<&Definition> {
516        self.definitions().find(|d| d.name() == name)
517    }
518
519    #[inline]
520    pub fn datatype_definitions(&self) -> impl Iterator<Item = &DatatypeDef> {
521        self.definitions.iter().filter_map(|d| match d {
522            Definition::Datatype(v) => Some(v),
523            _ => None,
524        })
525    }
526
527    #[inline]
528    pub fn entity_definitions(&self) -> impl Iterator<Item = &EntityDef> {
529        self.definitions.iter().filter_map(|d| match d {
530            Definition::Entity(v) => Some(v),
531            _ => None,
532        })
533    }
534
535    #[inline]
536    pub fn enum_definitions(&self) -> impl Iterator<Item = &EnumDef> {
537        self.definitions.iter().filter_map(|d| match d {
538            Definition::Enum(v) => Some(v),
539            _ => None,
540        })
541    }
542
543    #[inline]
544    pub fn event_definitions(&self) -> impl Iterator<Item = &EventDef> {
545        self.definitions.iter().filter_map(|d| match d {
546            Definition::Event(v) => Some(v),
547            _ => None,
548        })
549    }
550
551    #[inline]
552    pub fn property_definitions(&self) -> impl Iterator<Item = &PropertyDef> {
553        self.definitions.iter().filter_map(|d| match d {
554            Definition::Property(v) => Some(v),
555            _ => None,
556        })
557    }
558
559    #[inline]
560    pub fn rdf_definitions(&self) -> impl Iterator<Item = &RdfDef> {
561        self.definitions.iter().filter_map(|d| match d {
562            Definition::Rdf(v) => Some(v),
563            _ => None,
564        })
565    }
566
567    #[inline]
568    pub fn structure_definitions(&self) -> impl Iterator<Item = &StructureDef> {
569        self.definitions.iter().filter_map(|d| match d {
570            Definition::Structure(v) => Some(v),
571            _ => None,
572        })
573    }
574
575    #[inline]
576    pub fn type_class_definitions(&self) -> impl Iterator<Item = &TypeClassDef> {
577        self.definitions.iter().filter_map(|d| match d {
578            Definition::TypeClass(v) => Some(v),
579            _ => None,
580        })
581    }
582
583    #[inline]
584    pub fn union_definitions(&self) -> impl Iterator<Item = &UnionDef> {
585        self.definitions.iter().filter_map(|d| match d {
586            Definition::Union(v) => Some(v),
587            _ => None,
588        })
589    }
590
591    pub fn defined_names(&self) -> HashSet<&Identifier> {
592        self.definitions().map(|def| def.name()).collect()
593    }
594}
595
596// ------------------------------------------------------------------------------------------------
597// Implementations ❱ Modules ❱ Imports
598// ------------------------------------------------------------------------------------------------
599
600impl From<Import> for ImportStatement {
601    fn from(value: Import) -> Self {
602        Self::new(vec![value])
603    }
604}
605
606impl From<Vec<Import>> for ImportStatement {
607    fn from(value: Vec<Import>) -> Self {
608        Self::new(value)
609    }
610}
611
612impl FromIterator<Import> for ImportStatement {
613    fn from_iter<T: IntoIterator<Item = Import>>(iter: T) -> Self {
614        Self::new(Vec::from_iter(iter))
615    }
616}
617
618impl From<ImportStatement> for Vec<Import> {
619    fn from(value: ImportStatement) -> Self {
620        value.imports
621    }
622}
623
624impl_has_source_span_for! {ImportStatement}
625
626impl Validate for ImportStatement {
627    ///
628    /// # Checks
629    ///
630    /// - For each [`Import`]:
631    ///   - If module import:
632    ///     1. Ensure it is in the cache
633    ///     1. If the import has a version URI ensure the imported module has a matching one
634    ///     1.
635    ///     1.
636    ///     1.
637    ///
638    ///
639    ///
640    fn validate(
641        &self,
642        top: &Module,
643        cache: &impl ModuleStore,
644        loader: &impl ModuleLoader,
645        _: bool,
646    ) {
647        for import in self.imports() {
648            match import {
649                Import::Module(module_ref) => {
650                    module_ref
651                        .name()
652                        .validate(top, loader, Some(IdentifierCaseConvention::Module));
653                    if let Some(actual_module) = cache.get(module_ref.name()) {
654                        match (module_ref.version_uri(), actual_module.version_uri()) {
655                            (None, _) => {}
656                            (Some(expected), Some(actual)) => {
657                                if actual != expected {
658                                    loader
659                                        .report(&module_version_mismatch(
660                                            top.file_id().copied().unwrap_or_default(),
661                                            expected.source_span().map(|s| s.byte_range()),
662                                            expected.as_ref().to_string(),
663                                            actual_module.file_id().copied().unwrap_or_default(),
664                                            actual.source_span().map(|s| s.byte_range()),
665                                            actual.as_ref().to_string(),
666                                        ))
667                                        .unwrap();
668                                }
669                            }
670                            (Some(expected), None) => {
671                                loader
672                                    .report(&module_version_not_found(
673                                        top.file_id().copied().unwrap_or_default(),
674                                        module_ref.source_span().map(|s| s.byte_range()),
675                                        expected.as_ref().to_string(),
676                                        actual_module.file_id().copied().unwrap_or_default(),
677                                        actual_module.source_span().map(|s| s.byte_range()),
678                                        actual_module.name(),
679                                    ))
680                                    .unwrap();
681                            }
682                        }
683                    } else {
684                        loader
685                            .report(&imported_module_not_found(
686                                top.file_id().copied().unwrap_or_default(),
687                                module_ref.source_span().map(|s| s.byte_range()),
688                                module_ref.name(),
689                            ))
690                            .unwrap();
691                    }
692                }
693                Import::Member(id_ref) => {
694                    id_ref.validate(top, loader);
695                    if let Some(actual_module) = cache.get(id_ref.module()) {
696                        if actual_module.resolve_local(id_ref.member()).is_none() {
697                            loader
698                                .report(&definition_not_found(
699                                    top.file_id().copied().unwrap_or_default(),
700                                    id_ref.source_span().map(|s| s.byte_range()),
701                                    id_ref,
702                                ))
703                                .unwrap();
704                        }
705                    } else {
706                        loader
707                            .report(&imported_module_not_found(
708                                top.file_id().copied().unwrap_or_default(),
709                                id_ref.source_span().map(|s| s.byte_range()),
710                                id_ref,
711                            ))
712                            .unwrap();
713                    }
714                }
715            }
716        }
717    }
718}
719
720impl ImportStatement {
721    // --------------------------------------------------------------------------------------------
722    // ImportStatement :: Constructors
723    // --------------------------------------------------------------------------------------------
724
725    pub const fn new(imports: Vec<Import>) -> Self {
726        Self {
727            span: None,
728            imports,
729        }
730    }
731
732    pub fn new_module(import: Identifier) -> Self {
733        Self {
734            span: None,
735            imports: vec![Import::from(ModuleImport::from(import))],
736        }
737    }
738
739    pub fn new_module_with_version_uri(import: Identifier, version_uri: Url) -> Self {
740        Self {
741            span: None,
742            imports: vec![Import::from(
743                ModuleImport::from(import).with_version_uri(version_uri.into()),
744            )],
745        }
746    }
747
748    pub fn new_member(import: QualifiedIdentifier) -> Self {
749        Self {
750            span: None,
751            imports: vec![Import::from(import)],
752        }
753    }
754
755    // --------------------------------------------------------------------------------------------
756    // ImportStatement :: Fields
757    // --------------------------------------------------------------------------------------------
758
759    get_and_set_vec!(
760        pub
761        has has_imports,
762        imports_len,
763        imports,
764        imports_mut,
765        add_to_imports,
766        extend_imports
767            => imports, Import
768    );
769
770    // --------------------------------------------------------------------------------------------
771
772    pub fn as_slice(&self) -> &[Import] {
773        self.imports.as_slice()
774    }
775
776    // --------------------------------------------------------------------------------------------
777
778    pub fn imported_modules(&self) -> HashSet<&Identifier> {
779        self.imports()
780            .map(|imp| match imp {
781                Import::Module(v) => v.name(),
782                Import::Member(v) => v.module(),
783            })
784            .collect()
785    }
786
787    pub fn imported_module_versions(&self) -> HashMap<&Identifier, Option<&HeaderValue<Url>>> {
788        HashMap::from_iter(self.imports().map(|imp| match imp {
789            Import::Module(v) => (v.name(), v.version_uri()),
790            Import::Member(v) => (v.module(), None),
791        }))
792    }
793
794    pub fn imported_types(&self) -> HashSet<&QualifiedIdentifier> {
795        self.imports()
796            .filter_map(|imp| {
797                if let Import::Member(imp) = imp {
798                    Some(imp)
799                } else {
800                    None
801                }
802            })
803            .collect()
804    }
805}
806
807// ------------------------------------------------------------------------------------------------
808
809impl From<Identifier> for Import {
810    fn from(v: Identifier) -> Self {
811        Self::Module(ModuleImport::from(v))
812    }
813}
814
815impl From<ModuleImport> for Import {
816    fn from(v: ModuleImport) -> Self {
817        Self::Module(v)
818    }
819}
820
821impl From<QualifiedIdentifier> for Import {
822    fn from(v: QualifiedIdentifier) -> Self {
823        Self::Member(v)
824    }
825}
826
827enum_display_impl!(Import => Module, Member);
828
829impl_has_source_span_for!(Import => variants Module, Member);
830
831impl Import {
832    pub fn module(&self) -> &Identifier {
833        match self {
834            Import::Module(v) => v.name(),
835            Import::Member(v) => v.module(),
836        }
837    }
838    pub fn member(&self) -> Option<&Identifier> {
839        match self {
840            Import::Module(_) => None,
841            Import::Member(v) => Some(v.member()),
842        }
843    }
844}
845
846// ------------------------------------------------------------------------------------------------
847
848impl Display for ModuleImport {
849    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
850        write!(
851            f,
852            "{}",
853            if let Some(version_uri) = self.version_uri() {
854                format!("{} version {}", self.name(), version_uri)
855            } else {
856                self.name().to_string()
857            }
858        )
859    }
860}
861
862impl From<Identifier> for ModuleImport {
863    fn from(value: Identifier) -> Self {
864        Self::new(value)
865    }
866}
867
868impl PartialEq for ModuleImport {
869    fn eq(&self, other: &Self) -> bool {
870        self.name == other.name && self.version_uri == other.version_uri
871    }
872}
873
874impl Eq for ModuleImport {}
875
876impl Hash for ModuleImport {
877    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
878        // ignore: self.span.hash(state);
879        self.name.hash(state);
880        self.version_uri.hash(state);
881    }
882}
883
884impl_has_source_span_for!(ModuleImport);
885
886impl ModuleImport {
887    // --------------------------------------------------------------------------------------------
888    // ModuleImport :: Constructors
889    // --------------------------------------------------------------------------------------------
890    pub const fn new(name: Identifier) -> Self {
891        Self {
892            span: None,
893            name,
894            version_uri: None,
895        }
896    }
897
898    pub fn with_version_uri(self, version_uri: HeaderValue<Url>) -> Self {
899        Self {
900            version_uri: Some(version_uri),
901            ..self
902        }
903    }
904
905    // --------------------------------------------------------------------------------------------
906    // ModuleImport :: Fields
907    // --------------------------------------------------------------------------------------------
908
909    get_and_set!(pub name, set_name => Identifier);
910
911    get_and_set!(pub version_uri, set_version_uri, unset_version_uri => optional has_version_uri, HeaderValue<Url>);
912
913    // --------------------------------------------------------------------------------------------
914    // ModuleImport :: Helpers
915    // --------------------------------------------------------------------------------------------
916
917    pub fn eq_with_span(&self, other: &Self) -> bool {
918        self.span == other.span && self.name == other.name && self.version_uri == other.version_uri
919    }
920}