microcad_lang/model/attribute/
mod.rs1mod attributes;
7mod export_command;
8mod layer;
9mod measure_command;
10mod resolution_attribute;
11
12use std::rc::Rc;
13
14pub use attributes::Attributes;
15pub use export_command::ExportCommand;
16pub use layer::Layer;
17pub use measure_command::MeasureCommand;
18pub use resolution_attribute::ResolutionAttribute;
19
20use crate::{create_tuple_value, syntax::*, value::*};
21
22use microcad_core::{Color, Size2, theme::Theme};
23
24#[derive(Clone, Debug)]
26pub struct CustomCommand {
27 pub id: Identifier,
29 pub arguments: Box<Tuple>,
31}
32
33impl std::fmt::Display for CustomCommand {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 write!(f, "{} = {}", self.id, self.arguments)
36 }
37}
38
39#[derive(Clone, Debug)]
41pub enum Attribute {
42 Color(Color),
44 Resolution(ResolutionAttribute),
46 Theme(Rc<Theme>),
48 Size(Size2),
50 Export(ExportCommand),
52 Measure(MeasureCommand),
54 Custom(CustomCommand),
56}
57
58impl Attribute {
59 fn id(&self) -> Identifier {
61 match &self {
62 Attribute::Color(_) => Identifier::no_ref("color"),
63 Attribute::Resolution(_) => Identifier::no_ref("resolution"),
64 Attribute::Theme(_) => Identifier::no_ref("theme"),
65 Attribute::Size(_) => Identifier::no_ref("size"),
66 Attribute::Export(_) => Identifier::no_ref("export"),
67 Attribute::Measure(_) => Identifier::no_ref("measure"),
68 Attribute::Custom(attr) => attr.id.clone(),
69 }
70 }
71
72 pub fn is_unique(&self) -> bool {
74 matches!(
75 self,
76 Attribute::Color(_)
77 | Attribute::Resolution(_)
78 | Attribute::Theme(_)
79 | Attribute::Size(_)
80 )
81 }
82}
83
84impl std::fmt::Display for Attribute {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 write!(
87 f,
88 "#[{id} = {value}]",
89 id = self.id(),
90 value = match &self {
91 Attribute::Color(color) => format!("{color}"),
93 Attribute::Resolution(resolution) => format!("{resolution}"),
94 Attribute::Theme(theme) => theme.name.clone(),
95 Attribute::Size(size) => format!("{size}"),
96 Attribute::Export(export) => format!("{export}"),
97 Attribute::Measure(measure) => format!("{measure}"),
98 Attribute::Custom(command) => format!("{command}"),
99 }
100 )
101 }
102}
103
104impl From<Attribute> for Value {
106 fn from(value: Attribute) -> Self {
107 match value {
108 Attribute::Color(color) => Value::Tuple(Box::new(color.into())),
109 Attribute::Resolution(resolution_attribute) => resolution_attribute.into(),
110 Attribute::Theme(theme) => theme.into(),
111 Attribute::Size(size) => size.into(),
112 Attribute::Export(e) => e.into(),
113 Attribute::Measure(m) => m.into(),
114 Attribute::Custom(attr) => Value::Tuple(attr.arguments.clone()),
115 }
116 }
117}
118
119impl PartialEq for Attribute {
120 fn eq(&self, other: &Self) -> bool {
121 self.id() == other.id()
122 }
123}
124
125impl From<Rc<Theme>> for Value {
126 fn from(theme: Rc<Theme>) -> Self {
127 create_tuple_value!(
128 background = theme.background,
129 name = theme.name.clone(),
130 filename = theme.filename.clone().unwrap_or_default()
131 )
132 }
133}
134
135pub trait AttributesAccess {
137 fn get_attributes_by_id(&self, id: &Identifier) -> Vec<Attribute>;
139
140 fn get_single_attribute(&self, id: &Identifier) -> Option<Attribute> {
142 let attributes = self.get_attributes_by_id(id);
143 match attributes.len() {
144 1 => attributes.first().cloned(),
145 _ => None,
146 }
147 }
148
149 fn get_attribute_value(&self, id: &Identifier) -> Value {
151 match self.get_single_attribute(id) {
152 Some(attribute) => attribute.into(),
153 None => Value::None,
154 }
155 }
156
157 fn get_resolution(&self) -> Option<ResolutionAttribute> {
159 match self.get_single_attribute(&Identifier::no_ref("resolution")) {
160 Some(value) => match value {
161 Attribute::Resolution(resolution) => Some(resolution),
162 _ => unreachable!(),
163 },
164 None => None,
165 }
166 }
167
168 fn get_color(&self) -> Option<Color> {
170 match self.get_single_attribute(&Identifier::no_ref("color")) {
171 Some(value) => match value {
172 Attribute::Color(color) => Some(color),
173 _ => unreachable!(),
174 },
175 None => None,
176 }
177 }
178
179 fn get_theme(&self) -> Option<std::rc::Rc<Theme>> {
181 self.get_single_attribute(&Identifier::no_ref("theme"))
182 .map(|attr| match attr {
183 Attribute::Theme(theme) => theme,
184 _ => unreachable!(),
185 })
186 }
187
188 fn get_size(&self) -> Option<Size2> {
190 self.get_single_attribute(&Identifier::no_ref("size"))
191 .map(|attr| match attr {
192 Attribute::Size(size) => size,
193 _ => unreachable!(),
194 })
195 }
196
197 fn get_exports(&self) -> Vec<ExportCommand> {
199 self.get_attributes_by_id(&Identifier::no_ref("export"))
200 .into_iter()
201 .fold(Vec::new(), |mut exports, command| {
202 match command {
203 Attribute::Export(export_command) => exports.push(export_command.clone()),
204 _ => unreachable!(),
205 }
206 exports
207 })
208 }
209
210 fn get_measures(&self) -> Vec<MeasureCommand> {
212 self.get_attributes_by_id(&Identifier::no_ref("measure"))
213 .iter()
214 .fold(Vec::new(), |mut measures, attribute| {
215 match attribute {
216 Attribute::Measure(measure_command) => measures.push(measure_command.clone()),
217 _ => unreachable!(),
218 }
219 measures
220 })
221 }
222
223 fn get_custom_attributes(&self, id: &Identifier) -> Vec<Tuple> {
225 self.get_attributes_by_id(id)
226 .iter()
227 .fold(Vec::new(), |mut attributes, attribute| {
228 match attribute {
229 Attribute::Custom(attr) => attributes.push(attr.arguments.as_ref().clone()),
230 _ => unreachable!(),
231 }
232 attributes
233 })
234 }
235}