miden_mast_package/package/
manifest.rs1use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_assembly_syntax::{
5 ast::{
6 self, AttributeSet, Path,
7 types::{FunctionType, Type},
8 },
9 library::Library,
10};
11#[cfg(all(feature = "arbitrary", test))]
12use miden_core::serde::{Deserializable, Serializable};
13use miden_core::{Word, utils::DisplayHex};
14#[cfg(feature = "arbitrary")]
15use proptest::prelude::{Strategy, any};
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18use thiserror::Error;
19
20use crate::{Dependency, PackageId};
21
22#[derive(Debug, Default, Clone, PartialEq, Eq)]
28#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
29#[cfg_attr(
30 all(feature = "arbitrary", test),
31 miden_test_serde_macros::serde_test(binary_serde(true), serde_test(false))
32)]
33pub struct PackageManifest {
34 #[cfg_attr(
36 feature = "arbitrary",
37 proptest(
38 strategy = "proptest::collection::vec(any::<PackageExport>(), 1..10).prop_filter_map(\"package exports must have unique paths\", |exports| PackageManifest::new(exports).ok().map(|manifest| manifest.exports))"
39 )
40 )]
41 pub(super) exports: BTreeMap<Arc<Path>, PackageExport>,
42 pub(super) dependencies: Vec<Dependency>,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Error)]
48pub enum ManifestValidationError {
49 #[error("duplicate export path '{0}' in package manifest")]
50 DuplicateExport(Arc<Path>),
51 #[error("duplicate dependency '{0}' in package manifest")]
52 DuplicateDependency(PackageId),
53}
54
55impl PackageManifest {
56 pub fn new(
59 exports: impl IntoIterator<Item = PackageExport>,
60 ) -> Result<Self, ManifestValidationError> {
61 let mut manifest = Self {
62 exports: Default::default(),
63 dependencies: Default::default(),
64 };
65 for export in exports {
66 manifest.add_export(export)?;
67 }
68
69 Ok(manifest)
70 }
71
72 pub fn from_library(library: &Library) -> Self {
74 use miden_assembly_syntax::library::LibraryExport;
75 use miden_core::mast::MastNodeExt;
76
77 Self::new(library.exports().map(|export| match export {
78 LibraryExport::Procedure(export) => {
79 let digest = library.mast_forest()[export.node].digest();
80 PackageExport::Procedure(ProcedureExport {
81 path: export.path.clone(),
82 digest,
83 signature: export.signature.clone(),
84 attributes: export.attributes.clone(),
85 })
86 },
87 LibraryExport::Constant(export) => PackageExport::Constant(ConstantExport {
88 path: export.path.clone(),
89 value: export.value.clone(),
90 }),
91 LibraryExport::Type(export) => PackageExport::Type(TypeExport {
92 path: export.path.clone(),
93 ty: export.ty.clone(),
94 }),
95 }))
96 .expect("library exports should have unique paths")
97 }
98
99 pub fn with_dependencies(
101 mut self,
102 dependencies: impl IntoIterator<Item = Dependency>,
103 ) -> Result<Self, ManifestValidationError> {
104 for dependency in dependencies {
105 self.add_dependency(dependency)?;
106 }
107
108 Ok(self)
109 }
110
111 pub fn add_dependency(
113 &mut self,
114 dependency: Dependency,
115 ) -> Result<(), ManifestValidationError> {
116 if self.dependencies.iter().any(|existing| existing.id() == dependency.id()) {
117 return Err(ManifestValidationError::DuplicateDependency(dependency.name));
118 }
119
120 self.dependencies.push(dependency);
121 Ok(())
122 }
123
124 pub fn num_dependencies(&self) -> usize {
126 self.dependencies.len()
127 }
128
129 pub fn dependencies(&self) -> impl Iterator<Item = &Dependency> {
131 self.dependencies.iter()
132 }
133
134 pub fn num_exports(&self) -> usize {
136 self.exports.len()
137 }
138
139 pub fn exports(&self) -> impl Iterator<Item = &PackageExport> {
141 self.exports.values()
142 }
143
144 pub fn get_export(&self, name: impl AsRef<Path>) -> Option<&PackageExport> {
146 self.exports.get(name.as_ref())
147 }
148
149 pub fn get_procedures_by_digest(
152 &self,
153 digest: &Word,
154 ) -> impl Iterator<Item = &ProcedureExport> + '_ {
155 let digest = *digest;
156 self.exports.values().filter_map(move |export| match export {
157 PackageExport::Procedure(export) if export.digest == digest => Some(export),
158 PackageExport::Procedure(_) => None,
159 PackageExport::Constant(_) | PackageExport::Type(_) => None,
160 })
161 }
162
163 fn add_export(&mut self, export: PackageExport) -> Result<(), ManifestValidationError> {
164 let path = export.path();
165 if self.exports.insert(path.clone(), export).is_some() {
166 return Err(ManifestValidationError::DuplicateExport(path));
167 }
168
169 Ok(())
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
175#[repr(u8)]
176#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
177#[cfg_attr(
178 all(feature = "arbitrary", test),
179 miden_test_serde_macros::serde_test(binary_serde(true))
180)]
181pub enum PackageExport {
182 Procedure(ProcedureExport) = 1,
184 Constant(ConstantExport),
186 Type(TypeExport),
188}
189
190impl PackageExport {
191 pub fn path(&self) -> Arc<Path> {
193 match self {
194 Self::Procedure(export) => export.path.clone(),
195 Self::Constant(export) => export.path.clone(),
196 Self::Type(export) => export.path.clone(),
197 }
198 }
199
200 pub fn namespace(&self) -> &Path {
204 match self {
205 Self::Procedure(ProcedureExport { path, .. })
206 | Self::Constant(ConstantExport { path, .. })
207 | Self::Type(TypeExport { path, .. }) => path.parent().unwrap(),
208 }
209 }
210
211 pub fn name(&self) -> &str {
215 match self {
216 Self::Procedure(ProcedureExport { path, .. })
217 | Self::Constant(ConstantExport { path, .. })
218 | Self::Type(TypeExport { path, .. }) => path.last().unwrap(),
219 }
220 }
221
222 #[inline]
224 pub fn is_procedure(&self) -> bool {
225 matches!(self, Self::Procedure(_))
226 }
227
228 #[inline]
230 pub fn is_constant(&self) -> bool {
231 matches!(self, Self::Constant(_))
232 }
233
234 #[inline]
236 pub fn is_type(&self) -> bool {
237 matches!(self, Self::Type(_))
238 }
239
240 pub(crate) const fn tag(&self) -> u8 {
241 unsafe { *(self as *const Self).cast::<u8>() }
248 }
249}
250
251#[cfg(feature = "arbitrary")]
252impl proptest::arbitrary::Arbitrary for PackageExport {
253 type Parameters = ();
254
255 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
256 use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
257
258 prop_oneof![
259 any::<ProcedureExport>().prop_map(Self::Procedure),
260 any::<ConstantExport>().prop_map(Self::Constant),
261 any::<TypeExport>().prop_map(Self::Type),
262 ]
263 .boxed()
264 }
265
266 type Strategy = proptest::prelude::BoxedStrategy<Self>;
267}
268
269#[derive(Clone, PartialEq, Eq)]
271#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
272#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
273#[cfg_attr(
274 all(feature = "arbitrary", test),
275 miden_test_serde_macros::serde_test(binary_serde(true))
276)]
277pub struct ProcedureExport {
278 #[cfg_attr(feature = "serde", serde(with = "miden_assembly_syntax::ast::path"))]
280 #[cfg_attr(
281 feature = "arbitrary",
282 proptest(strategy = "miden_assembly_syntax::arbitrary::path::bare_path_random_length(2)")
283 )]
284 pub path: Arc<Path>,
285 #[cfg_attr(feature = "arbitrary", proptest(value = "Word::default()"))]
287 pub digest: Word,
288 #[cfg_attr(feature = "arbitrary", proptest(value = "None"))]
290 #[cfg_attr(feature = "serde", serde(default))]
291 pub signature: Option<FunctionType>,
292 #[cfg_attr(feature = "arbitrary", proptest(value = "AttributeSet::default()"))]
294 #[cfg_attr(feature = "serde", serde(default))]
295 pub attributes: AttributeSet,
296}
297
298impl fmt::Debug for ProcedureExport {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 let Self { path, digest, signature, attributes } = self;
301 f.debug_struct("PackageExport")
302 .field("path", &format_args!("{path}"))
303 .field("digest", &format_args!("{}", DisplayHex::new(&digest.as_bytes())))
304 .field("signature", signature)
305 .field("attributes", attributes)
306 .finish()
307 }
308}
309
310#[derive(Clone, PartialEq, Eq)]
312#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
313#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
314#[cfg_attr(
315 all(feature = "arbitrary", test),
316 miden_test_serde_macros::serde_test(binary_serde(true))
317)]
318pub struct ConstantExport {
319 #[cfg_attr(feature = "serde", serde(with = "miden_assembly_syntax::ast::path"))]
321 #[cfg_attr(
322 feature = "arbitrary",
323 proptest(
324 strategy = "miden_assembly_syntax::arbitrary::path::constant_path_random_length(1)"
325 )
326 )]
327 pub path: Arc<Path>,
328 pub value: ast::ConstantValue,
335}
336
337impl fmt::Debug for ConstantExport {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 let Self { path, value } = self;
340 f.debug_struct("ConstantExport")
341 .field("path", &format_args!("{path}"))
342 .field("value", value)
343 .finish()
344 }
345}
346
347#[derive(Clone, PartialEq, Eq)]
349#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
350#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
351#[cfg_attr(
352 all(feature = "arbitrary", test),
353 miden_test_serde_macros::serde_test(binary_serde(true))
354)]
355pub struct TypeExport {
356 #[cfg_attr(feature = "serde", serde(with = "miden_assembly_syntax::ast::path"))]
358 #[cfg_attr(
359 feature = "arbitrary",
360 proptest(
361 strategy = "miden_assembly_syntax::arbitrary::path::user_defined_type_path_random_length(1)"
362 )
363 )]
364 pub path: Arc<Path>,
365 #[cfg_attr(feature = "arbitrary", proptest(value = "Type::Felt"))]
367 pub ty: Type,
368}
369
370impl fmt::Debug for TypeExport {
371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372 let Self { path, ty } = self;
373 f.debug_struct("TypeExport")
374 .field("path", &format_args!("{path}"))
375 .field("ty", ty)
376 .finish()
377 }
378}