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