miden_assembly_syntax/ast/attribute/meta/
kv.rs

1use alloc::collections::BTreeMap;
2use core::borrow::Borrow;
3
4use miden_core::utils::{
5    ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
6};
7use miden_debug_types::{SourceSpan, Spanned};
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use super::MetaExpr;
12use crate::ast::Ident;
13
14/// Represents the metadata of a key-value [crate::ast::Attribute], i.e. `@props(key = value)`
15#[derive(Debug, Clone)]
16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17pub struct MetaKeyValue {
18    #[cfg_attr(feature = "serde", serde(skip, default))]
19    pub span: SourceSpan,
20    /// The name of the key-value dictionary
21    pub name: Ident,
22    /// The set of key-value pairs provided as arguments to this attribute
23    pub items: BTreeMap<Ident, MetaExpr>,
24}
25
26impl Spanned for MetaKeyValue {
27    #[inline(always)]
28    fn span(&self) -> SourceSpan {
29        self.span
30    }
31}
32
33impl MetaKeyValue {
34    pub fn new<K, V, I>(name: Ident, items: I) -> Self
35    where
36        I: IntoIterator<Item = (K, V)>,
37        K: Into<Ident>,
38        V: Into<MetaExpr>,
39    {
40        let items = items.into_iter().map(|(k, v)| (k.into(), v.into())).collect();
41        Self { span: SourceSpan::default(), name, items }
42    }
43
44    pub fn with_span(mut self, span: SourceSpan) -> Self {
45        self.span = span;
46        self
47    }
48
49    /// Get the name of this metadata as a string
50    #[inline]
51    pub fn name(&self) -> &str {
52        self.name.as_str()
53    }
54
55    /// Get the name of this metadata as an [Ident]
56    #[inline]
57    pub fn id(&self) -> Ident {
58        self.name.clone()
59    }
60
61    /// Returns true if this metadata contains an entry for `key`
62    pub fn contains_key<Q>(&self, key: &Q) -> bool
63    where
64        Ident: Borrow<Q> + Ord,
65        Q: ?Sized + Ord,
66    {
67        self.items.contains_key(key)
68    }
69
70    /// Returns the value associated with `key`, if present in this metadata
71    pub fn get<Q>(&self, key: &Q) -> Option<&MetaExpr>
72    where
73        Ident: Borrow<Q> + Ord,
74        Q: ?Sized + Ord,
75    {
76        self.items.get(key)
77    }
78
79    /// Inserts a new key-value entry in this metadata
80    pub fn insert(&mut self, key: impl Into<Ident>, value: impl Into<MetaExpr>) {
81        self.items.insert(key.into(), value.into());
82    }
83
84    /// Removes the entry associated with `key`, if present in this metadata, and returns it
85    pub fn remove<Q>(&mut self, key: &Q) -> Option<MetaExpr>
86    where
87        Ident: Borrow<Q> + Ord,
88        Q: ?Sized + Ord,
89    {
90        self.items.remove(key)
91    }
92
93    /// Get an entry in the key-value map of this metadata for `key`
94    pub fn entry(
95        &mut self,
96        key: Ident,
97    ) -> alloc::collections::btree_map::Entry<'_, Ident, MetaExpr> {
98        self.items.entry(key)
99    }
100
101    /// Get an iterator over the the key-value items of this metadata
102    #[inline]
103    pub fn iter(&self) -> impl Iterator<Item = (&Ident, &MetaExpr)> {
104        self.items.iter()
105    }
106}
107
108impl IntoIterator for MetaKeyValue {
109    type Item = (Ident, MetaExpr);
110    type IntoIter = alloc::collections::btree_map::IntoIter<Ident, MetaExpr>;
111
112    #[inline]
113    fn into_iter(self) -> Self::IntoIter {
114        self.items.into_iter()
115    }
116}
117
118impl Eq for MetaKeyValue {}
119
120impl PartialEq for MetaKeyValue {
121    fn eq(&self, other: &Self) -> bool {
122        self.name == other.name && self.items == other.items
123    }
124}
125
126impl PartialOrd for MetaKeyValue {
127    #[inline]
128    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
129        Some(self.cmp(other))
130    }
131}
132
133impl Ord for MetaKeyValue {
134    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
135        self.name.cmp(&other.name).then_with(|| self.items.cmp(&other.items))
136    }
137}
138
139impl core::hash::Hash for MetaKeyValue {
140    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
141        self.name.hash(state);
142        self.items.hash(state);
143    }
144}
145
146#[cfg(feature = "arbitrary")]
147impl proptest::arbitrary::Arbitrary for MetaKeyValue {
148    type Parameters = ();
149
150    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
151        use proptest::{arbitrary::any, strategy::Strategy};
152
153        let name = any::<Ident>();
154        let items = proptest::collection::btree_map(any::<Ident>(), any::<MetaExpr>(), 1..3);
155        (name, items)
156            .prop_map(|(name, items)| Self { span: SourceSpan::UNKNOWN, name, items })
157            .boxed()
158    }
159
160    type Strategy = proptest::prelude::BoxedStrategy<Self>;
161}
162
163impl Serializable for MetaKeyValue {
164    fn write_into<W: ByteWriter>(&self, target: &mut W) {
165        self.name.write_into(target);
166        self.items.write_into(target);
167    }
168}
169
170impl Deserializable for MetaKeyValue {
171    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
172        let name = Ident::read_from(source)?;
173        let items = BTreeMap::read_from(source)?;
174
175        Ok(Self { span: SourceSpan::UNKNOWN, name, items })
176    }
177}