Skip to main content

miden_mast_package/package/
manifest.rs

1use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_assembly_syntax::ast::{
5    self, AttributeSet, Path,
6    types::{FunctionType, Type},
7};
8use miden_core::{Word, utils::DisplayHex};
9#[cfg(feature = "arbitrary")]
10use proptest::prelude::{Strategy, any};
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use crate::Dependency;
15
16// PACKAGE MANIFEST
17// ================================================================================================
18
19/// The manifest of a package, containing the set of package dependencies (libraries or packages)
20/// and exported items (procedures, constants, types), if known.
21#[derive(Debug, Default, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
23pub struct PackageManifest {
24    /// The set of exports in this package.
25    #[cfg_attr(
26        feature = "arbitrary",
27        proptest(
28            strategy = "proptest::collection::vec(any::<PackageExport>(), 1..10).prop_map(|exports| PackageManifest::new(exports).exports)"
29        )
30    )]
31    pub(super) exports: BTreeMap<Arc<Path>, PackageExport>,
32    /// The libraries (packages) linked against by this package, which must be provided when
33    /// executing the program.
34    pub(super) dependencies: Vec<Dependency>,
35}
36
37impl PackageManifest {
38    pub fn new(exports: impl IntoIterator<Item = PackageExport>) -> Self {
39        let exports = exports.into_iter().map(|export| (export.path(), export)).collect();
40        Self {
41            exports,
42            dependencies: Default::default(),
43        }
44    }
45
46    /// Extend this manifest with the provided dependencies
47    pub fn with_dependencies(mut self, dependencies: impl IntoIterator<Item = Dependency>) -> Self {
48        self.dependencies.extend(dependencies);
49        self
50    }
51
52    /// Add a dependency to the manifest
53    pub fn add_dependency(&mut self, dependency: Dependency) {
54        self.dependencies.push(dependency);
55    }
56
57    /// Get the number of dependencies of this package
58    pub fn num_dependencies(&self) -> usize {
59        self.dependencies.len()
60    }
61
62    /// Get an iterator over the dependencies of this package
63    pub fn dependencies(&self) -> impl Iterator<Item = &Dependency> {
64        self.dependencies.iter()
65    }
66
67    /// Get the number of items exported from this package
68    pub fn num_exports(&self) -> usize {
69        self.exports.len()
70    }
71
72    /// Get an iterator over the exports in this package
73    pub fn exports(&self) -> impl Iterator<Item = &PackageExport> {
74        self.exports.values()
75    }
76
77    /// Get information about an export by it's qualified name
78    pub fn get_export(&self, name: impl AsRef<Path>) -> Option<&PackageExport> {
79        self.exports.get(name.as_ref())
80    }
81
82    /// Get information about all exported procedures of this package with the given MAST root
83    /// digest
84    pub fn get_procedures_by_digest(
85        &self,
86        digest: &Word,
87    ) -> impl Iterator<Item = &ProcedureExport> + '_ {
88        let digest = *digest;
89        self.exports.values().filter_map(move |export| match export {
90            PackageExport::Procedure(export) if export.digest == digest => Some(export),
91            PackageExport::Procedure(_) => None,
92            PackageExport::Constant(_) | PackageExport::Type(_) => None,
93        })
94    }
95}
96
97/// Represents a named item exported from a package.
98#[derive(Debug, Clone, PartialEq, Eq)]
99#[repr(u8)]
100#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
101#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
102pub enum PackageExport {
103    /// A procedure definition or alias with 'pub' visibility
104    Procedure(ProcedureExport) = 1,
105    /// A constant definition with 'pub' visibility
106    Constant(ConstantExport),
107    /// A type declaration with 'pub' visibility
108    Type(TypeExport),
109}
110
111impl PackageExport {
112    /// Get the path of this exported item
113    pub fn path(&self) -> Arc<Path> {
114        match self {
115            Self::Procedure(export) => export.path.clone(),
116            Self::Constant(export) => export.path.clone(),
117            Self::Type(export) => export.path.clone(),
118        }
119    }
120
121    /// Get the namespace of the exported item.
122    ///
123    /// For example, if `Self::path` returns the path `std::foo::NAME`, this returns `std::foo`.
124    pub fn namespace(&self) -> &Path {
125        match self {
126            Self::Procedure(ProcedureExport { path, .. })
127            | Self::Constant(ConstantExport { path, .. })
128            | Self::Type(TypeExport { path, .. }) => path.parent().unwrap(),
129        }
130    }
131
132    /// Get the name of the exported item without its namespace.
133    ///
134    /// For example, if `Self::path` returns the path `std::foo::NAME`, this returns just `NAME`.
135    pub fn name(&self) -> &str {
136        match self {
137            Self::Procedure(ProcedureExport { path, .. })
138            | Self::Constant(ConstantExport { path, .. })
139            | Self::Type(TypeExport { path, .. }) => path.last().unwrap(),
140        }
141    }
142
143    /// Returns true if this item is a procedure
144    #[inline]
145    pub fn is_procedure(&self) -> bool {
146        matches!(self, Self::Procedure(_))
147    }
148
149    /// Returns true if this item is a constant
150    #[inline]
151    pub fn is_constant(&self) -> bool {
152        matches!(self, Self::Constant(_))
153    }
154
155    /// Returns true if this item is a type declaration
156    #[inline]
157    pub fn is_type(&self) -> bool {
158        matches!(self, Self::Type(_))
159    }
160
161    pub(crate) const fn tag(&self) -> u8 {
162        // SAFETY: This is safe because we have given this enum a
163        // primitive representation with #[repr(u8)], with the first
164        // field of the underlying union-of-structs the discriminant
165        //
166        // See the section on "accessing the numeric value of the discriminant"
167        // here: https://doc.rust-lang.org/std/mem/fn.discriminant.html
168        unsafe { *(self as *const Self).cast::<u8>() }
169    }
170}
171
172#[cfg(feature = "arbitrary")]
173impl proptest::arbitrary::Arbitrary for PackageExport {
174    type Parameters = ();
175
176    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
177        use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
178
179        prop_oneof![
180            any::<ProcedureExport>().prop_map(Self::Procedure),
181            any::<ConstantExport>().prop_map(Self::Constant),
182            any::<TypeExport>().prop_map(Self::Type),
183        ]
184        .boxed()
185    }
186
187    type Strategy = proptest::prelude::BoxedStrategy<Self>;
188}
189
190/// A procedure exported by a package, along with its digest, signature, and attributes.
191#[derive(Clone, PartialEq, Eq)]
192#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
193#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
194#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
195pub struct ProcedureExport {
196    /// The fully-qualified path of the procedure exported by this package.
197    #[cfg_attr(feature = "serde", serde(with = "miden_assembly_syntax::ast::path"))]
198    #[cfg_attr(
199        feature = "arbitrary",
200        proptest(strategy = "miden_assembly_syntax::arbitrary::path::bare_path_random_length(2)")
201    )]
202    pub path: Arc<Path>,
203    /// The digest of the procedure exported by this package.
204    #[cfg_attr(feature = "arbitrary", proptest(value = "Word::default()"))]
205    pub digest: Word,
206    /// The type signature of the exported procedure.
207    #[cfg_attr(feature = "arbitrary", proptest(value = "None"))]
208    #[cfg_attr(feature = "serde", serde(default))]
209    pub signature: Option<FunctionType>,
210    /// Attributes attached to the exported procedure.
211    #[cfg_attr(feature = "arbitrary", proptest(value = "AttributeSet::default()"))]
212    #[cfg_attr(feature = "serde", serde(default))]
213    pub attributes: AttributeSet,
214}
215
216impl fmt::Debug for ProcedureExport {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        let Self { path, digest, signature, attributes } = self;
219        f.debug_struct("PackageExport")
220            .field("path", &format_args!("{path}"))
221            .field("digest", &format_args!("{}", DisplayHex::new(&digest.as_bytes())))
222            .field("signature", signature)
223            .field("attributes", attributes)
224            .finish()
225    }
226}
227
228/// A constant definition exported by a package
229#[derive(Clone, PartialEq, Eq)]
230#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
231#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
232#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
233pub struct ConstantExport {
234    /// The fully-qualified path of the constant exported by this package.
235    #[cfg_attr(feature = "serde", serde(with = "miden_assembly_syntax::ast::path"))]
236    #[cfg_attr(
237        feature = "arbitrary",
238        proptest(
239            strategy = "miden_assembly_syntax::arbitrary::path::constant_path_random_length(1)"
240        )
241    )]
242    pub path: Arc<Path>,
243    /// The value of the exported constant
244    ///
245    /// We export a [ast::ConstantValue] here, rather than raw felts, because it is how a constant
246    /// is used that determines its final concrete value, not the declaration itself. However,
247    /// [ast::ConstantValue] does represent a concrete value, just one that requires context to
248    /// fully evaluate.
249    pub value: ast::ConstantValue,
250}
251
252impl fmt::Debug for ConstantExport {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        let Self { path, value } = self;
255        f.debug_struct("ConstantExport")
256            .field("path", &format_args!("{path}"))
257            .field("value", value)
258            .finish()
259    }
260}
261
262/// A named type declaration exported by a package
263#[derive(Clone, PartialEq, Eq)]
264#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
265#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
266#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
267pub struct TypeExport {
268    /// The fully-qualified path of the type exported by this package.
269    #[cfg_attr(feature = "serde", serde(with = "miden_assembly_syntax::ast::path"))]
270    #[cfg_attr(
271        feature = "arbitrary",
272        proptest(
273            strategy = "miden_assembly_syntax::arbitrary::path::user_defined_type_path_random_length(1)"
274        )
275    )]
276    pub path: Arc<Path>,
277    /// The type that was declared
278    #[cfg_attr(feature = "arbitrary", proptest(value = "Type::Felt"))]
279    pub ty: Type,
280}
281
282impl fmt::Debug for TypeExport {
283    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284        let Self { path, ty } = self;
285        f.debug_struct("TypeExport")
286            .field("path", &format_args!("{path}"))
287            .field("ty", ty)
288            .finish()
289    }
290}