Skip to main content

microcad_lang/syntax/identifier/
qualified_name.rs

1// Copyright © 2024-2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{src_ref::*, syntax::*};
5use derive_more::{Deref, DerefMut};
6use miette::SourceSpan;
7
8/// A *qualified name* consists of a list of *identifiers*, separated by `::`,
9/// e.g. `a::b::c`
10#[derive(Default, Clone, PartialEq, Hash, Eq, Ord, PartialOrd, DerefMut, Deref)]
11pub struct QualifiedName(Refer<Vec<Identifier>>);
12
13impl QualifiedName {
14    /// If the qualified name is a single identifier, return it
15    pub fn as_identifier(&self) -> Option<&Identifier> {
16        self.0.first().filter(|_| self.0.len() == 1)
17    }
18}
19
20/// List of *qualified names* which can be displayed.
21#[derive(Deref)]
22pub struct QualifiedNames(Vec<QualifiedName>);
23
24impl std::fmt::Display for QualifiedNames {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(
27            f,
28            "{}",
29            self.0
30                .iter()
31                .map(|name| name.to_string())
32                .collect::<Vec<_>>()
33                .join(", ")
34        )
35    }
36}
37
38impl std::fmt::Debug for QualifiedNames {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(
41            f,
42            "{}",
43            self.0
44                .iter()
45                .map(|name| format!("{:?}", name.to_string()))
46                .collect::<Vec<_>>()
47                .join(", ")
48        )
49    }
50}
51
52pub(crate) type QualifiedNameSet = indexmap::IndexSet<QualifiedName>;
53
54impl FromIterator<QualifiedName> for QualifiedNames {
55    fn from_iter<T: IntoIterator<Item = QualifiedName>>(iter: T) -> Self {
56        Self(iter.into_iter().collect())
57    }
58}
59
60impl QualifiedName {
61    /// Create [`QualifiedName`] from [`Identifier`]s.
62    ///
63    /// - `ids`: *Identifiers* that concatenate to the *qualified name*.
64    /// - `src_ref`: Reference for the whole name.
65    pub fn new(ids: Vec<Identifier>, src_ref: SrcRef) -> Self {
66        Self(Refer::new(ids, src_ref))
67    }
68
69    /// Create [`QualifiedName`] from single [`Identifier`].
70    pub fn from_id(id: Identifier) -> Self {
71        let src_ref = id.src_ref();
72        Self(Refer::new(vec![id], src_ref))
73    }
74
75    /// Create *qualified name* from [`identifier`]s without source code reference.
76    ///
77    /// - `ids`: *Identifiers* that concatenate to the *qualified name*.
78    pub fn no_ref(ids: Vec<Identifier>) -> Self {
79        Self(Refer::none(ids))
80    }
81
82    /// Returns true if self is a qualified name with multiple ids in it
83    pub fn is_qualified(&self) -> bool {
84        self.0.len() > 1
85    }
86
87    /// Returns true if name contains exactly one id
88    pub fn is_id(&self) -> bool {
89        self.0.len() == 1
90    }
91
92    /// Tells if self is in a specified module
93    pub fn is_within(&self, module: &QualifiedName) -> bool {
94        self.starts_with(module)
95    }
96
97    /// Returns `true` if this name is in builtin module
98    pub fn is_builtin(&self) -> bool {
99        if let Some(first) = self.first() {
100            first == "__builtin"
101        } else {
102            false
103        }
104    }
105
106    /// remove the first name from path
107    pub fn remove_first(&self) -> Self {
108        Self(Refer::new(self.0[1..].to_vec(), self.0.src_ref.clone()))
109    }
110
111    /// remove the first name from path
112    pub fn remove_last(self) -> Self {
113        Self(Refer::new(
114            self.0[..self.0.len() - 1].to_vec(),
115            self.0.src_ref.clone(),
116        ))
117    }
118
119    /// Append identifier to name
120    pub fn push(&mut self, id: Identifier) {
121        self.0.push(id)
122    }
123
124    /// Split name into first id and the rest
125    pub fn split_first(&self) -> (Identifier, QualifiedName) {
126        match self.len() {
127            0 => todo!("return None or error?"),
128            1 => (self.0[0].clone(), Self::default()),
129            _ => (self.0[0].clone(), Self(Refer::none(self.0[1..].into()))),
130        }
131    }
132
133    /// return basename, `std::geo2d` returns `std`
134    pub fn basename(&self) -> Option<Self> {
135        let mut s = self.clone();
136        if s.len() >= 2 {
137            s.pop();
138            Some(s)
139        } else {
140            None
141        }
142    }
143
144    /// Return the base of the given relative name.
145    pub fn base(&self, relative: &Self) -> Self {
146        if self == relative {
147            QualifiedName::default()
148        } else {
149            assert!(!relative.is_empty());
150            assert!(self.len() > relative.len());
151            assert!(self.ends_with(relative));
152            let (base, _) = self.split_at(self.len() - relative.len());
153            base.iter().cloned().collect()
154        }
155    }
156
157    /// Add given prefix to name
158    pub fn with_prefix(&self, prefix: &QualifiedName) -> Self {
159        let mut full_name = prefix.clone();
160        full_name.append(&mut self.clone());
161        full_name
162    }
163
164    /// Add a given identifier as suffix.
165    pub fn with_suffix(&self, suffix: &Identifier) -> Self {
166        let mut name = self.clone();
167        name.push(suffix.clone());
168        name
169    }
170
171    pub(crate) fn count_super(&self) -> usize {
172        self.iter().take_while(|id| id.is_super()).count()
173    }
174
175    pub(crate) fn un_super(&self) -> Self {
176        self.iter().filter(|id| !id.is_super()).cloned().collect()
177    }
178}
179
180impl SingleIdentifier for QualifiedName {
181    fn single_identifier(&self) -> Option<&Identifier> {
182        if self.is_single_identifier() {
183            self.0.first()
184        } else {
185            None
186        }
187    }
188
189    fn is_single_identifier(&self) -> bool {
190        self.0.len() == 1
191    }
192}
193
194impl From<QualifiedName> for SourceSpan {
195    fn from(value: QualifiedName) -> Self {
196        value.src_ref().into()
197    }
198}
199
200#[test]
201fn test_base() {
202    let d: QualifiedName = "a::b::c::d".into();
203    assert_eq!(d.base(&"b::c::d".into()), "a".into());
204    assert_eq!(d.base(&"c::d".into()), "a::b".into());
205    assert_eq!(d.base(&"d".into()), "a::b::c".into());
206}
207
208#[test]
209#[should_panic]
210fn test_base_panic() {
211    let d: QualifiedName = "a::b::c::d".into();
212    assert_eq!(d.base(&"a::b::c::d".into()), "".into());
213}
214
215#[test]
216fn dissolve_super() {
217    let what: QualifiedName = "super::super::c::x".into();
218    assert_eq!(what.count_super(), 2);
219}
220
221impl std::fmt::Display for QualifiedName {
222    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
223        if self.is_empty() {
224            write!(f, crate::invalid_no_ansi!(NAME))
225        } else {
226            write!(
227                f,
228                "{}",
229                self.iter()
230                    .map(|id| format!("{id}"))
231                    .collect::<Vec<_>>()
232                    .join("::")
233            )
234        }
235    }
236}
237
238impl std::fmt::Debug for QualifiedName {
239    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
240        if self.is_empty() {
241            write!(f, crate::invalid!(NAME))
242        } else {
243            write!(
244                f,
245                "{}",
246                self.iter()
247                    .map(|id| format!("{id:?}"))
248                    .collect::<Vec<_>>()
249                    .join("::")
250            )
251        }
252    }
253}
254
255impl SrcReferrer for QualifiedName {
256    fn src_ref(&self) -> SrcRef {
257        self.0.src_ref()
258    }
259}
260
261impl From<Refer<Vec<Identifier>>> for QualifiedName {
262    fn from(value: Refer<Vec<Identifier>>) -> Self {
263        Self(value)
264    }
265}
266
267impl FromIterator<Identifier> for QualifiedName {
268    fn from_iter<T: IntoIterator<Item = Identifier>>(iter: T) -> Self {
269        Self(Refer::none(iter.into_iter().collect()))
270    }
271}
272
273impl From<&Identifier> for QualifiedName {
274    fn from(id: &Identifier) -> Self {
275        Self(Refer::none(vec![id.clone()]))
276    }
277}
278
279impl From<&std::path::Path> for QualifiedName {
280    fn from(path: &std::path::Path) -> Self {
281        // check if this is a module file and remove doublet module generation
282        let path = if path.file_stem() == Some(std::ffi::OsStr::new("mod")) {
283            path.parent().expect("mod file in root path is not allowed")
284        } else {
285            path
286        };
287
288        QualifiedName::no_ref(
289            path.iter()
290                .map(|id| {
291                    Identifier(Refer {
292                        value: id.to_string_lossy().into_owned().into(),
293                        src_ref: SrcRef(None),
294                    })
295                })
296                .collect(),
297        )
298    }
299}
300
301#[cfg(test)]
302impl From<&str> for QualifiedName {
303    fn from(value: &str) -> Self {
304        Self(Refer::none(
305            value.split("::").map(Identifier::from).collect(),
306        ))
307    }
308}
309
310#[cfg(not(test))]
311impl TryFrom<&str> for QualifiedName {
312    type Error = ();
313
314    fn try_from(value: &str) -> Result<Self, Self::Error> {
315        let mut name = Vec::new();
316        for id in value.split("::").map(Identifier::try_from) {
317            if id.is_err() {
318                return Err(());
319            }
320            name.push(id.expect("unexpected error"));
321        }
322
323        Ok(Self(Refer::none(name)))
324    }
325}
326
327impl From<Identifier> for QualifiedName {
328    fn from(id: Identifier) -> Self {
329        let src_ref = id.src_ref();
330        QualifiedName(Refer::new(vec![id], src_ref))
331    }
332}
333
334impl From<QualifiedName> for String {
335    fn from(value: QualifiedName) -> Self {
336        value
337            .iter()
338            .map(|id| format!("{id}"))
339            .collect::<Vec<_>>()
340            .join("::")
341    }
342}
343
344impl TreeDisplay for QualifiedName {
345    fn tree_print(&self, f: &mut std::fmt::Formatter, depth: TreeState) -> std::fmt::Result {
346        writeln!(
347            f,
348            "{:depth$}QualifiedName: '{}'",
349            "",
350            self.iter()
351                .map(|id| format!("{id:?}"))
352                .collect::<Vec<_>>()
353                .join("::")
354        )
355    }
356}