microcad_lang/syntax/identifier/
qualified_name.rs

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