miden_assembly_syntax/ast/attribute/
mod.rs1mod meta;
2mod set;
3
4use core::fmt;
5
6use miden_core::serde::{
7 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
8};
9use miden_debug_types::{SourceSpan, Spanned};
10
11pub use self::{
12 meta::{BorrowedMeta, Meta, MetaExpr, MetaItem, MetaKeyValue, MetaList},
13 set::{AttributeSet, AttributeSetEntry},
14};
15use crate::{ast::Ident, prettier};
16
17#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40#[cfg_attr(
41 all(feature = "arbitrary", test),
42 miden_test_serialization_macros::serialization_test
43)]
44pub enum Attribute {
45 Marker(Ident),
47 List(MetaList),
49 KeyValue(MetaKeyValue),
51}
52
53impl Attribute {
54 pub fn new(name: Ident, metadata: impl Into<Meta>) -> Self {
65 let metadata = metadata.into();
66 match metadata {
67 Meta::Unit => Self::Marker(name),
68 Meta::List(items) => Self::List(MetaList { span: Default::default(), name, items }),
69 Meta::KeyValue(items) => {
70 Self::KeyValue(MetaKeyValue { span: Default::default(), name, items })
71 },
72 }
73 }
74
75 pub fn from_iter<V, I>(name: Ident, metadata: I) -> Self
81 where
82 Meta: FromIterator<V>,
83 I: IntoIterator<Item = V>,
84 {
85 Self::new(name, Meta::from_iter(metadata))
86 }
87
88 pub fn with_span(self, span: SourceSpan) -> Self {
90 match self {
91 Self::Marker(id) => Self::Marker(id.with_span(span)),
92 Self::List(list) => Self::List(list.with_span(span)),
93 Self::KeyValue(kv) => Self::KeyValue(kv.with_span(span)),
94 }
95 }
96
97 pub fn name(&self) -> &str {
99 match self {
100 Self::Marker(id) => id.as_str(),
101 Self::List(list) => list.name(),
102 Self::KeyValue(kv) => kv.name(),
103 }
104 }
105
106 pub fn id(&self) -> Ident {
108 match self {
109 Self::Marker(id) => id.clone(),
110 Self::List(list) => list.id(),
111 Self::KeyValue(kv) => kv.id(),
112 }
113 }
114
115 pub fn is_marker(&self) -> bool {
117 matches!(self, Self::Marker(_))
118 }
119
120 pub fn is_list(&self) -> bool {
122 matches!(self, Self::List(_))
123 }
124
125 pub fn is_key_value(&self) -> bool {
127 matches!(self, Self::KeyValue(_))
128 }
129
130 pub fn metadata(&self) -> Option<BorrowedMeta<'_>> {
134 match self {
135 Self::Marker(_) => None,
136 Self::List(list) => Some(BorrowedMeta::List(&list.items)),
137 Self::KeyValue(kv) => Some(BorrowedMeta::KeyValue(&kv.items)),
138 }
139 }
140}
141
142impl fmt::Debug for Attribute {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 match self {
145 Self::Marker(id) => f.debug_tuple("Marker").field(&id).finish(),
146 Self::List(meta) => f
147 .debug_struct("List")
148 .field("name", &meta.name)
149 .field("items", &meta.items)
150 .finish(),
151 Self::KeyValue(meta) => f
152 .debug_struct("KeyValue")
153 .field("name", &meta.name)
154 .field("items", &meta.items)
155 .finish(),
156 }
157 }
158}
159
160impl fmt::Display for Attribute {
161 #[inline]
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 use prettier::PrettyPrint;
164 self.pretty_print(f)
165 }
166}
167
168impl prettier::PrettyPrint for Attribute {
169 fn render(&self) -> prettier::Document {
170 use prettier::*;
171 let doc = text(format!("@{}", self.name()));
172 match self {
173 Self::Marker(_) => doc,
174 Self::List(meta) => {
175 let singleline_items = meta
176 .items
177 .iter()
178 .map(PrettyPrint::render)
179 .reduce(|acc, item| acc + const_text(", ") + item)
180 .unwrap_or(Document::Empty);
181 let multiline_items = indent(
182 4,
183 nl() + meta
184 .items
185 .iter()
186 .map(PrettyPrint::render)
187 .reduce(|acc, item| acc + nl() + item)
188 .unwrap_or(Document::Empty),
189 ) + nl();
190 doc + const_text("(") + (singleline_items | multiline_items) + const_text(")")
191 },
192 Self::KeyValue(meta) => {
193 let singleline_items = meta
194 .items
195 .iter()
196 .map(|(k, v)| text(k) + const_text(" = ") + v.render())
197 .reduce(|acc, item| acc + const_text(", ") + item)
198 .unwrap_or(Document::Empty);
199 let multiline_items = indent(
200 4,
201 nl() + meta
202 .items
203 .iter()
204 .map(|(k, v)| text(k) + const_text(" = ") + v.render())
205 .reduce(|acc, item| acc + nl() + item)
206 .unwrap_or(Document::Empty),
207 ) + nl();
208 doc + const_text("(") + (singleline_items | multiline_items) + const_text(")")
209 },
210 }
211 }
212}
213
214impl Spanned for Attribute {
215 fn span(&self) -> SourceSpan {
216 match self {
217 Self::Marker(id) => id.span(),
218 Self::List(list) => list.span(),
219 Self::KeyValue(kv) => kv.span(),
220 }
221 }
222}
223
224impl From<Ident> for Attribute {
225 fn from(value: Ident) -> Self {
226 Self::Marker(value)
227 }
228}
229
230impl<K, V> From<(K, V)> for Attribute
231where
232 K: Into<Ident>,
233 V: Into<MetaExpr>,
234{
235 fn from(kv: (K, V)) -> Self {
236 let (key, value) = kv;
237 Self::List(MetaList {
238 span: SourceSpan::default(),
239 name: key.into(),
240 items: vec![value.into()],
241 })
242 }
243}
244
245impl From<MetaList> for Attribute {
246 fn from(value: MetaList) -> Self {
247 Self::List(value)
248 }
249}
250
251impl From<MetaKeyValue> for Attribute {
252 fn from(value: MetaKeyValue) -> Self {
253 Self::KeyValue(value)
254 }
255}
256
257#[cfg(feature = "arbitrary")]
258impl proptest::arbitrary::Arbitrary for Attribute {
259 type Parameters = ();
260
261 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
262 use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
263
264 prop_oneof![
265 any::<Ident>().prop_map(Self::Marker),
266 any::<MetaList>().prop_map(Self::List),
267 any::<MetaKeyValue>().prop_map(Self::KeyValue),
268 ]
269 .boxed()
270 }
271
272 type Strategy = proptest::prelude::BoxedStrategy<Self>;
273}
274
275impl Serializable for Attribute {
276 fn write_into<W: ByteWriter>(&self, target: &mut W) {
277 match self {
278 Self::Marker(name) => {
279 target.write_u8(0);
280 name.write_into(target);
281 },
282 Self::List(list) => {
283 target.write_u8(1);
284 list.write_into(target);
285 },
286 Self::KeyValue(kv) => {
287 target.write_u8(2);
288 kv.write_into(target);
289 },
290 }
291 }
292}
293
294impl Deserializable for Attribute {
295 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
296 match source.read_u8()? {
297 0 => Ident::read_from(source).map(Self::Marker),
298 1 => MetaList::read_from(source).map(Self::List),
299 2 => MetaKeyValue::read_from(source).map(Self::KeyValue),
300 n => Err(DeserializationError::InvalidValue(format!(
301 "unknown Attribute variant tag '{n}'"
302 ))),
303 }
304 }
305}