1use alloc::{
23 format,
24 string::{String, ToString},
25 sync::Arc,
26 vec::Vec,
27};
28
29use miden_assembly_syntax::{
30 Library,
31 ast::{AttributeSet, PathBuf},
32};
33use miden_core::{
34 Word,
35 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
36};
37
38use super::{ConstantExport, PackageId, ProcedureExport, TargetType, TypeExport};
39use crate::{Dependency, Package, PackageExport, PackageManifest, Section};
40
41const MAGIC_PACKAGE: &[u8; 5] = b"MASP\0";
46
47const VERSION: [u8; 3] = [4, 0, 0];
51
52impl Serializable for Package {
56 fn write_into<W: ByteWriter>(&self, target: &mut W) {
57 target.write_bytes(MAGIC_PACKAGE);
59 target.write_bytes(&VERSION);
60
61 self.name.write_into(target);
63
64 self.version.to_string().write_into(target);
66
67 self.description.write_into(target);
69
70 target.write_u8(self.kind.into());
72
73 self.mast.write_into(target);
75
76 self.manifest.write_into(target);
78
79 target.write_usize(self.sections.len());
81 for section in self.sections.iter() {
82 section.write_into(target);
83 }
84 }
85}
86
87impl Deserializable for Package {
88 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
89 let magic: [u8; 5] = source.read_array()?;
91 if magic != *MAGIC_PACKAGE {
92 return Err(DeserializationError::InvalidValue(format!(
93 "invalid magic bytes. Expected '{MAGIC_PACKAGE:?}', got '{magic:?}'"
94 )));
95 }
96
97 let version: [u8; 3] = source.read_array()?;
98 if version != VERSION {
99 return Err(DeserializationError::InvalidValue(format!(
100 "unsupported version. Got '{version:?}', but only '{VERSION:?}' is supported"
101 )));
102 }
103
104 let name = PackageId::read_from(source)?;
106
107 let version = String::read_from(source)?
109 .parse::<crate::Version>()
110 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
111
112 let description = Option::<String>::read_from(source)?;
114
115 let kind_tag = source.read_u8()?;
117 let kind = TargetType::try_from(kind_tag)
118 .map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
119
120 let mast = Arc::new(Library::read_from(source)?);
122
123 let manifest = PackageManifest::read_from(source)?;
125
126 let sections = Vec::<Section>::read_from(source)?;
128
129 Ok(Self {
130 name,
131 version,
132 description,
133 kind,
134 mast,
135 manifest,
136 sections,
137 })
138 }
139}
140
141#[cfg(feature = "serde")]
145impl serde::Serialize for PackageManifest {
146 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
147 where
148 S: serde::Serializer,
149 {
150 use alloc::collections::BTreeMap;
151
152 use miden_assembly_syntax::Path;
153 use serde::ser::SerializeStruct;
154
155 struct PackageExports<'a>(&'a BTreeMap<Arc<Path>, PackageExport>);
156
157 impl serde::Serialize for PackageExports<'_> {
158 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159 where
160 S: serde::Serializer,
161 {
162 use serde::ser::SerializeSeq;
163
164 let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
165 for value in self.0.values() {
166 serializer.serialize_element(value)?;
167 }
168 serializer.end()
169 }
170 }
171
172 let mut serializer = serializer.serialize_struct("PackageManifest", 2)?;
173 serializer.serialize_field("exports", &PackageExports(&self.exports))?;
174 serializer.serialize_field("dependencies", &self.dependencies)?;
175 serializer.end()
176 }
177}
178
179#[cfg(feature = "serde")]
180impl<'de> serde::Deserialize<'de> for PackageManifest {
181 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182 where
183 D: serde::Deserializer<'de>,
184 {
185 #[derive(serde::Deserialize)]
186 #[serde(field_identifier, rename_all = "lowercase")]
187 enum Field {
188 Exports,
189 Dependencies,
190 }
191
192 struct PackageManifestVisitor;
193
194 impl<'de> serde::de::Visitor<'de> for PackageManifestVisitor {
195 type Value = PackageManifest;
196
197 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
198 formatter.write_str("struct PackageManifest")
199 }
200
201 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
202 where
203 A: serde::de::SeqAccess<'de>,
204 {
205 let exports = seq
206 .next_element::<Vec<PackageExport>>()?
207 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
208 let dependencies = seq
209 .next_element::<Vec<Dependency>>()?
210 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
211 PackageManifest::new(exports)
212 .and_then(|manifest| manifest.with_dependencies(dependencies))
213 .map_err(serde::de::Error::custom)
214 }
215
216 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
217 where
218 A: serde::de::MapAccess<'de>,
219 {
220 let mut exports = None;
221 let mut dependencies = None;
222 while let Some(key) = map.next_key()? {
223 match key {
224 Field::Exports => {
225 if exports.is_some() {
226 return Err(serde::de::Error::duplicate_field("exports"));
227 }
228 exports = Some(map.next_value::<Vec<PackageExport>>()?);
229 },
230 Field::Dependencies => {
231 if dependencies.is_some() {
232 return Err(serde::de::Error::duplicate_field("dependencies"));
233 }
234 dependencies = Some(map.next_value::<Vec<Dependency>>()?);
235 },
236 }
237 }
238 let exports = exports.ok_or_else(|| serde::de::Error::missing_field("exports"))?;
239 let dependencies =
240 dependencies.ok_or_else(|| serde::de::Error::missing_field("dependencies"))?;
241 PackageManifest::new(exports)
242 .and_then(|manifest| manifest.with_dependencies(dependencies))
243 .map_err(serde::de::Error::custom)
244 }
245 }
246
247 deserializer.deserialize_struct(
248 "PackageManifest",
249 &["exports", "dependencies"],
250 PackageManifestVisitor,
251 )
252 }
253}
254
255impl Serializable for PackageManifest {
256 fn write_into<W: ByteWriter>(&self, target: &mut W) {
257 target.write_usize(self.num_exports());
259 for export in self.exports() {
260 export.write_into(target);
261 }
262
263 target.write_usize(self.num_dependencies());
265 for dep in self.dependencies() {
266 dep.write_into(target);
267 }
268 }
269}
270
271impl Deserializable for PackageManifest {
272 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
273 let exports_len = source.read_usize()?;
275 let mut exports = Vec::with_capacity(exports_len);
276 for _ in 0..exports_len {
277 exports.push(PackageExport::read_from(source)?);
278 }
279
280 let dependencies = Vec::<Dependency>::read_from(source)?;
282
283 PackageManifest::new(exports)
284 .and_then(|manifest| manifest.with_dependencies(dependencies))
285 .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
286 }
287}
288
289impl Serializable for PackageExport {
292 fn write_into<W: ByteWriter>(&self, target: &mut W) {
293 target.write_u8(self.tag());
294 match self {
295 Self::Procedure(export) => export.write_into(target),
296 Self::Constant(export) => export.write_into(target),
297 Self::Type(export) => export.write_into(target),
298 }
299 }
300}
301
302impl Deserializable for PackageExport {
303 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
304 match source.read_u8()? {
305 1 => ProcedureExport::read_from(source).map(Self::Procedure),
306 2 => ConstantExport::read_from(source).map(Self::Constant),
307 3 => TypeExport::read_from(source).map(Self::Type),
308 invalid => Err(DeserializationError::InvalidValue(format!(
309 "unexpected PackageExport tag: '{invalid}'"
310 ))),
311 }
312 }
313}
314
315impl Serializable for ProcedureExport {
316 fn write_into<W: ByteWriter>(&self, target: &mut W) {
317 self.path.write_into(target);
318 self.digest.write_into(target);
319 match self.signature.as_ref() {
320 Some(sig) => {
321 target.write_bool(true);
322 sig.write_into(target);
323 },
324 None => {
325 target.write_bool(false);
326 },
327 }
328 self.attributes.write_into(target);
329 }
330}
331
332impl Deserializable for ProcedureExport {
333 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
334 use miden_assembly_syntax::ast::types::FunctionType;
335 let path = PathBuf::read_from(source)?.into_boxed_path().into();
336 let digest = Word::read_from(source)?;
337 let signature = if source.read_bool()? {
338 Some(FunctionType::read_from(source)?)
339 } else {
340 None
341 };
342 let attributes = AttributeSet::read_from(source)?;
343 Ok(Self { path, digest, signature, attributes })
344 }
345}
346
347impl Serializable for ConstantExport {
348 fn write_into<W: ByteWriter>(&self, target: &mut W) {
349 self.path.write_into(target);
350 self.value.write_into(target);
351 }
352}
353
354impl Deserializable for ConstantExport {
355 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
356 let path = PathBuf::read_from(source)?.into_boxed_path().into();
357 let value = miden_assembly_syntax::ast::ConstantValue::read_from(source)?;
358 Ok(Self { path, value })
359 }
360}
361
362impl Serializable for TypeExport {
363 fn write_into<W: ByteWriter>(&self, target: &mut W) {
364 self.path.write_into(target);
365 self.ty.write_into(target);
366 }
367}
368
369impl Deserializable for TypeExport {
370 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
371 use miden_assembly_syntax::ast::types::Type;
372 let path = PathBuf::read_from(source)?.into_boxed_path().into();
373 let ty = Type::read_from(source)?;
374 Ok(Self { path, ty })
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use alloc::{collections::BTreeMap, string::ToString, sync::Arc, vec, vec::Vec};
381
382 use miden_assembly_syntax::{
383 Library,
384 ast::{AttributeSet, Path as AstPath, PathBuf},
385 library::{LibraryExport, ProcedureExport as LibraryProcedureExport},
386 };
387 use miden_core::{
388 mast::{BasicBlockNodeBuilder, MastForest, MastForestContributor, MastNodeExt, MastNodeId},
389 operations::Operation,
390 serde::{
391 BudgetedReader, ByteWriter, Deserializable, DeserializationError, Serializable,
392 SliceReader,
393 },
394 };
395 #[cfg(feature = "serde")]
396 use serde_json::{json, to_value};
397
398 use super::{MAGIC_PACKAGE, Package, PackageExport, PackageManifest, VERSION};
399 use crate::{
400 Dependency, ManifestValidationError, PackageId, TargetType,
401 package::manifest::ProcedureExport as PackageProcedureExport,
402 };
403
404 fn build_forest() -> (MastForest, MastNodeId) {
405 let mut forest = MastForest::new();
406 let node_id = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
407 .add_to_forest(&mut forest)
408 .expect("failed to build basic block");
409 forest.make_root(node_id);
410 (forest, node_id)
411 }
412
413 fn absolute_path(name: &str) -> Arc<AstPath> {
414 let path = PathBuf::new(name).expect("invalid path");
415 let path = path.as_path().to_absolute().into_owned();
416 Arc::from(path.into_boxed_path())
417 }
418
419 fn build_library() -> Arc<Library> {
420 let (forest, node_id) = build_forest();
421 let path = absolute_path("test::proc");
422 let export = LibraryProcedureExport::new(node_id, Arc::clone(&path));
423
424 let mut exports = BTreeMap::new();
425 exports.insert(path, LibraryExport::Procedure(export));
426
427 Arc::new(Library::new(Arc::new(forest), exports).expect("failed to build library"))
428 }
429
430 fn build_package() -> Package {
431 let library = build_library();
432 let path = absolute_path("test::proc");
433 let node_id = library.get_export_node_id(path.as_ref());
434 let digest = library.mast_forest()[node_id].digest();
435
436 let export = PackageExport::Procedure(PackageProcedureExport {
437 path: Arc::clone(&path),
438 digest,
439 signature: None,
440 attributes: AttributeSet::default(),
441 });
442
443 let manifest =
444 PackageManifest::new([export]).expect("test package manifest should be valid");
445
446 Package {
447 name: PackageId::from("test_pkg"),
448 version: crate::Version::new(0, 0, 0),
449 description: None,
450 kind: TargetType::Library,
451 mast: library,
452 manifest,
453 sections: Vec::new(),
454 }
455 }
456
457 fn build_dependency() -> Dependency {
458 Dependency {
459 name: PackageId::from("dep"),
460 kind: TargetType::Library,
461 version: crate::Version::new(1, 0, 0),
462 digest: Default::default(),
463 }
464 }
465
466 fn package_bytes_with_sections_count(count: usize) -> Vec<u8> {
467 let package = build_package();
468 let mut bytes = Vec::new();
469
470 bytes.write_bytes(MAGIC_PACKAGE);
471 bytes.write_bytes(&VERSION);
472 package.name.write_into(&mut bytes);
473 package.version.to_string().write_into(&mut bytes);
474 package.description.write_into(&mut bytes);
475 bytes.write_u8(package.kind.into());
476 package.mast.write_into(&mut bytes);
477 package.manifest.write_into(&mut bytes);
478 bytes.write_usize(count);
479
480 bytes
481 }
482
483 #[test]
484 fn package_manifest_rejects_over_budget_dependencies() {
485 let mut bytes = Vec::new();
486 bytes.write_usize(0);
487 bytes.write_usize(2);
488
489 let mut reader = BudgetedReader::new(SliceReader::new(&bytes), 2);
490 let err = PackageManifest::read_from(&mut reader).unwrap_err();
491 assert!(matches!(err, DeserializationError::InvalidValue(_)));
492 }
493
494 #[test]
495 fn package_rejects_over_budget_sections() {
496 let bytes = package_bytes_with_sections_count(2);
497 let mut reader = BudgetedReader::new(SliceReader::new(&bytes), bytes.len());
498 let err = Package::read_from(&mut reader).unwrap_err();
499 assert!(matches!(err, DeserializationError::InvalidValue(_)));
500 }
501
502 #[test]
503 fn package_manifest_new_rejects_duplicate_export_paths() {
504 let library = build_library();
505 let path = absolute_path("test::proc");
506 let node_id = library.get_export_node_id(path.as_ref());
507 let digest = library.mast_forest()[node_id].digest();
508 let export = PackageExport::Procedure(PackageProcedureExport {
509 path: path.clone(),
510 digest,
511 signature: None,
512 attributes: AttributeSet::default(),
513 });
514
515 let err = PackageManifest::new([export.clone(), export])
516 .expect_err("duplicate export paths should be rejected by constructors");
517 assert_eq!(err, ManifestValidationError::DuplicateExport(path));
518 }
519
520 #[test]
521 fn package_manifest_add_dependency_rejects_duplicate_dependencies() {
522 let mut manifest =
523 PackageManifest::new([]).expect("empty package manifest should be valid");
524 let dependency = build_dependency();
525
526 manifest
527 .add_dependency(dependency.clone())
528 .expect("first dependency should be accepted");
529 let err = manifest
530 .add_dependency(dependency)
531 .expect_err("duplicate dependencies should be rejected by helpers");
532 assert_eq!(err, ManifestValidationError::DuplicateDependency(PackageId::from("dep")));
533 }
534
535 #[test]
536 fn package_manifest_rejects_duplicate_export_paths() {
537 let library = build_library();
538 let path = absolute_path("test::proc");
539 let node_id = library.get_export_node_id(path.as_ref());
540 let digest = library.mast_forest()[node_id].digest();
541 let export = PackageExport::Procedure(PackageProcedureExport {
542 path,
543 digest,
544 signature: None,
545 attributes: AttributeSet::default(),
546 });
547
548 let mut bytes = Vec::new();
549 bytes.write_usize(2);
550 export.write_into(&mut bytes);
551 export.write_into(&mut bytes);
552 bytes.write_usize(0);
553
554 let mut reader = SliceReader::new(&bytes);
555 let err = PackageManifest::read_from(&mut reader)
556 .expect_err("duplicate export paths should be rejected during deserialization");
557 assert!(matches!(err, DeserializationError::InvalidValue(_)));
558 }
559
560 #[test]
561 fn package_manifest_rejects_duplicate_dependencies() {
562 let dependency = build_dependency();
563
564 let mut bytes = Vec::new();
565 bytes.write_usize(0);
566 bytes.write_usize(2);
567 dependency.write_into(&mut bytes);
568 dependency.write_into(&mut bytes);
569
570 let mut reader = SliceReader::new(&bytes);
571 let err = PackageManifest::read_from(&mut reader)
572 .expect_err("duplicate dependencies should be rejected during deserialization");
573 assert!(matches!(err, DeserializationError::InvalidValue(_)));
574 }
575
576 #[cfg(feature = "serde")]
577 #[test]
578 fn serde_package_manifest_rejects_duplicate_export_paths() {
579 let library = build_library();
580 let path = absolute_path("test::proc");
581 let node_id = library.get_export_node_id(path.as_ref());
582 let digest = library.mast_forest()[node_id].digest();
583 let export = PackageExport::Procedure(PackageProcedureExport {
584 path,
585 digest,
586 signature: None,
587 attributes: AttributeSet::default(),
588 });
589 let export = to_value(&export).expect("export should serialize");
590
591 let manifest = serde_json::to_string(&json!({
592 "exports": [export.clone(), export],
593 "dependencies": [],
594 }))
595 .expect("manifest should serialize to JSON");
596 let err = serde_json::from_str::<PackageManifest>(&manifest)
597 .expect_err("serde deserialization should reject duplicate export paths");
598 let message = err.to_string();
599 assert!(message.contains("duplicate export path"));
600 }
601
602 #[cfg(feature = "serde")]
603 #[test]
604 fn serde_package_manifest_rejects_duplicate_dependencies() {
605 let dependency = to_value(build_dependency()).expect("dependency should serialize");
606
607 let manifest = serde_json::to_string(&json!({
608 "exports": [],
609 "dependencies": [dependency.clone(), dependency],
610 }))
611 .expect("manifest should serialize to JSON");
612 let err = serde_json::from_str::<PackageManifest>(&manifest)
613 .expect_err("serde deserialization should reject duplicate dependencies");
614 let message = err.to_string();
615 assert!(message.contains("duplicate dependency"));
616 }
617}