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