1use crate::{
2 diagnostics::{Diagnostics, WarningType},
3 fun::Name,
4};
5use indexmap::{IndexMap, IndexSet};
6use itertools::Itertools;
7use std::fmt::Display;
8
9pub mod book;
10pub mod loader;
11pub mod packages;
12
13pub use loader::*;
14
15pub type BindMap = IndexMap<Name, Name>;
16
17#[derive(Debug, Clone, Default)]
18pub struct ImportCtx {
19 imports: Vec<Import>,
21
22 map: ImportsMap,
24}
25
26impl ImportCtx {
27 pub fn add_import(&mut self, import: Import) {
28 self.imports.push(import);
29 }
30
31 pub fn to_imports(self) -> Vec<Import> {
32 self.imports
33 }
34
35 pub fn sources(&self) -> Vec<&Name> {
36 let mut names = Vec::new();
37 for imps in &self.imports {
38 match &imps.src {
39 BoundSource::None => {}
40 BoundSource::File(f) => names.push(f),
41 BoundSource::Dir(v) => names.extend(v.values()),
42 BoundSource::Either(_, _) => unreachable!("This should be resolved into `File` or `Dir` by now"),
43 }
44 }
45 names
46 }
47}
48
49#[derive(Debug, Clone)]
50pub struct Import {
51 pub path: Name,
52 pub imp_type: ImportType,
53 pub relative: bool,
54 pub src: BoundSource,
55}
56
57impl Import {
58 pub fn new(path: Name, imp_type: ImportType, relative: bool) -> Self {
59 Self { path, imp_type, relative, src: BoundSource::None }
60 }
61}
62
63#[derive(Debug, Clone)]
64pub enum ImportType {
65 Single(Name, Option<Name>),
66 List(Vec<(Name, Option<Name>)>),
67 Glob,
68}
69
70impl Display for ImportType {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 match self {
73 ImportType::Single(n, _) => write!(f, "{n}"),
74 ImportType::List(l) => write!(f, "({})", l.iter().map(|(n, _)| n).join(", ")),
75 ImportType::Glob => write!(f, "*"),
76 }
77 }
78}
79
80#[derive(Debug, Clone)]
81pub enum BoundSource {
82 None,
83 File(Name),
84 Dir(IndexMap<Name, Name>),
85 Either(Name, IndexMap<Name, Name>),
87}
88
89#[derive(Debug, Clone, Default)]
90struct ImportsMap {
91 binds: BindMap,
92}
93
94impl ImportsMap {
95 pub fn contains_source(&self, s: &Name) -> bool {
96 self.binds.values().contains(s)
97 }
98
99 fn add_bind(&mut self, src: &str, bind: Name, diag: &mut Diagnostics) {
100 if let Some(old) = self.binds.get(&bind) {
101 let warn = format!("The import '{src}' shadows the imported name '{old}'");
102 diag.add_book_warning(warn, WarningType::ImportShadow);
103 }
104
105 self.binds.insert(bind, Name::new(src));
106 }
107
108 fn add_aliased_bind(&mut self, src: &Name, sub: &Name, alias: Option<&Name>, diag: &mut Diagnostics) {
109 let src = format!("{}/{}", src, sub);
110 let aliased = alias.unwrap_or(sub);
111 self.add_bind(&src, aliased.clone(), diag);
112 }
113
114 fn add_binds(&mut self, names: &IndexSet<Name>, src: &Name, diag: &mut Diagnostics) {
115 for sub in names {
116 self.add_aliased_bind(src, sub, None, diag);
117 }
118 }
119
120 fn add_file_nested_binds(
123 &mut self,
124 src: &Name,
125 file: &Name,
126 alias: Option<&Name>,
127 names: IndexSet<Name>,
128 diag: &mut Diagnostics,
129 ) {
130 let aliased = alias.unwrap_or(file);
131
132 self.add_nested_binds(src, aliased, names.iter().filter(|&n| n != file), diag);
133
134 if names.contains(file) {
135 let src = format!("{}/{}", src, file);
136 self.add_bind(&src, aliased.clone(), diag);
137 }
138 }
139
140 fn add_nested_binds<'a>(
142 &mut self,
143 src: &Name,
144 bind: &Name,
145 names: impl Iterator<Item = &'a Name>,
146 diag: &mut Diagnostics,
147 ) {
148 for name in names {
149 let src = format!("{}/{}", src, name);
150 let bind = Name::new(format!("{bind}/{name}"));
151 self.add_bind(&src, bind, diag);
152 }
153 }
154}