Skip to main content

microcad_lang/syntax/
attribute.rs

1// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Attribute syntax entities.
5
6use crate::syntax::*;
7use derive_more::{Deref, DerefMut};
8use microcad_lang_base::{SrcRef, SrcReferrer, TreeDisplay, TreeState};
9
10/// *Command syntax* within an attribute.
11#[derive(Clone)]
12pub enum AttributeCommand {
13    /// A bare name
14    Ident(Identifier),
15    /// A command with optional arguments: `width(offset = 30mm)`.
16    Call(Call),
17    /// An assignment: `color = "red"`.
18    Assigment {
19        /// Source code reference.
20        src_ref: SrcRef,
21        /// Name of the assigment.
22        name: Identifier,
23        /// Value name of the assigment.
24        value: Expression,
25    },
26}
27
28impl Identifiable for AttributeCommand {
29    /// Id of the attribute command
30    fn id_ref(&self) -> &Identifier {
31        match self {
32            AttributeCommand::Ident(name) => name,
33            AttributeCommand::Call(call) => call
34                .name
35                .as_identifier()
36                .expect("non-identifier attribute call"),
37            AttributeCommand::Assigment { name, .. } => name,
38        }
39    }
40}
41
42impl std::fmt::Display for AttributeCommand {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        match &self {
45            AttributeCommand::Ident(name) => write!(f, "{name}"),
46            AttributeCommand::Call(call) => write!(f, "{call}"),
47            AttributeCommand::Assigment { name, value, .. } => write!(f, "{name} = {value}"),
48        }
49    }
50}
51
52impl std::fmt::Debug for AttributeCommand {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        match &self {
55            AttributeCommand::Ident(name) => write!(f, "{name:?}"),
56            AttributeCommand::Call(call) => write!(f, "{call:?}"),
57            AttributeCommand::Assigment { name, value, .. } => write!(f, "{name:?} = {value:?}"),
58        }
59    }
60}
61
62impl SrcReferrer for AttributeCommand {
63    fn src_ref(&self) -> SrcRef {
64        match &self {
65            AttributeCommand::Ident(name) => name.src_ref(),
66            AttributeCommand::Call(call) => call.src_ref(),
67            AttributeCommand::Assigment { src_ref, .. } => src_ref.clone(),
68        }
69    }
70}
71
72/// An attribute item.
73#[derive(Clone)]
74pub struct Attribute {
75    /// Attribute commands: `export = "test.stl", height(30mm)`.
76    pub commands: Vec<AttributeCommand>,
77    /// Tells if the attribute is an inner attribute: `#[...]` (outer) vs `#![...]` (inner).
78    pub is_inner: bool,
79    /// Source reference
80    pub src_ref: SrcRef,
81}
82
83impl Attribute {
84    /// Return some command it is the only one in the list.
85    pub fn single_command(&self) -> Option<&AttributeCommand> {
86        match self.commands.len() {
87            1 => self.commands.first(),
88            _ => None,
89        }
90    }
91}
92
93impl TreeDisplay for Attribute {
94    fn tree_print(&self, f: &mut std::fmt::Formatter, depth: TreeState) -> std::fmt::Result {
95        writeln!(f, "{:depth$}Attribute: {self}", "")
96    }
97}
98
99impl std::fmt::Display for Attribute {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        match self.is_inner {
102            true => write!(f, "#![")?,
103            false => write!(f, "#[")?,
104        }
105        write!(
106            f,
107            "{}",
108            self.commands
109                .iter()
110                .map(|command| command.to_string())
111                .collect::<Vec<_>>()
112                .join(", ")
113        )?;
114        writeln!(f, "]")
115    }
116}
117
118impl std::fmt::Debug for Attribute {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        write!(f, "{self}")
121    }
122}
123
124impl SrcReferrer for Attribute {
125    fn src_ref(&self) -> SrcRef {
126        self.src_ref.clone()
127    }
128}
129
130/// A list of attributes, e.g. `#foo #[bar, baz = 42]`
131#[derive(Clone, Default, Deref, DerefMut)]
132pub struct AttributeList(Vec<Attribute>);
133
134impl From<Vec<Attribute>> for AttributeList {
135    fn from(value: Vec<Attribute>) -> Self {
136        AttributeList(value)
137    }
138}
139
140impl std::fmt::Display for AttributeList {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        self.0.iter().try_for_each(|attr| writeln!(f, "{attr}"))
143    }
144}
145
146impl std::fmt::Debug for AttributeList {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        write!(f, "{self}")
149    }
150}
151
152impl SrcReferrer for AttributeList {
153    fn src_ref(&self) -> SrcRef {
154        if self.0.is_empty() {
155            SrcRef(None)
156        } else {
157            SrcRef::merge(
158                &self.0.first().expect("One element").src_ref(),
159                &self.0.last().expect("Second element").src_ref(),
160            )
161        }
162    }
163}