miden_mast_package/package/
mod.rs

1mod manifest;
2mod section;
3mod serialization;
4
5use alloc::{format, string::String, sync::Arc, vec::Vec};
6
7use miden_assembly_syntax::{Library, Report, ast::QualifiedProcedureName};
8pub use miden_assembly_syntax::{Version, VersionError};
9use miden_core::{Program, Word};
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13pub use self::{
14    manifest::{PackageExport, PackageManifest},
15    section::{InvalidSectionIdError, Section, SectionId},
16};
17use crate::MastArtifact;
18
19// PACKAGE
20// ================================================================================================
21
22/// A package containing a [Program]/[Library], and a manifest (exports and dependencies).
23#[derive(Debug, Clone, Eq, PartialEq)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct Package {
26    /// Name of the package
27    pub name: String,
28    /// An optional semantic version for the package
29    #[cfg_attr(feature = "serde", serde(default))]
30    pub version: Option<Version>,
31    /// An optional description of the package
32    #[cfg_attr(feature = "serde", serde(default))]
33    pub description: Option<String>,
34    /// The MAST artifact ([Program] or [Library]) of the package
35    pub mast: MastArtifact,
36    /// The package manifest, containing the set of exported procedures and their signatures,
37    /// if known.
38    pub manifest: PackageManifest,
39    /// The set of custom sections included with the package, e.g. debug information, account
40    /// metadata, etc.
41    #[cfg_attr(feature = "serde", serde(default))]
42    pub sections: Vec<Section>,
43}
44
45impl Package {
46    /// Returns the digest of the package's MAST artifact
47    pub fn digest(&self) -> Word {
48        self.mast.digest()
49    }
50
51    /// Returns the MastArtifact of the package
52    pub fn into_mast_artifact(self) -> MastArtifact {
53        self.mast
54    }
55
56    /// Checks if the package's MAST artifact is a [Program]
57    pub fn is_program(&self) -> bool {
58        matches!(self.mast, MastArtifact::Executable(_))
59    }
60
61    /// Checks if the package's MAST artifact is a [Library]
62    pub fn is_library(&self) -> bool {
63        matches!(self.mast, MastArtifact::Library(_))
64    }
65
66    /// Unwraps the package's MAST artifact as a [Program] or panics if it is a [Library]
67    pub fn unwrap_program(&self) -> Arc<Program> {
68        match self.mast {
69            MastArtifact::Executable(ref prog) => Arc::clone(prog),
70            _ => panic!("expected package to contain a program, but got a library"),
71        }
72    }
73
74    /// Unwraps the package's MAST artifact as a [Library] or panics if it is a [Program]
75    pub fn unwrap_library(&self) -> Arc<Library> {
76        match self.mast {
77            MastArtifact::Library(ref lib) => Arc::clone(lib),
78            _ => panic!("expected package to contain a library, but got an executable"),
79        }
80    }
81
82    /// Creates a new package with [Program] from this [Library] package and the given
83    /// entrypoint (should be a procedure in the library).
84    pub fn make_executable(&self, entrypoint: &QualifiedProcedureName) -> Result<Self, Report> {
85        let MastArtifact::Library(ref library) = self.mast else {
86            return Err(Report::msg("expected library but got an executable"));
87        };
88
89        let module = library
90            .module_infos()
91            .find(|info| info.path() == &entrypoint.module)
92            .ok_or_else(|| {
93                Report::msg(format!(
94                    "invalid entrypoint: library does not contain a module named '{}'",
95                    entrypoint.module
96                ))
97            })?;
98        if let Some(digest) = module.get_procedure_digest_by_name(&entrypoint.name) {
99            let node_id = library.mast_forest().find_procedure_root(digest).ok_or_else(|| {
100                Report::msg(
101                    "invalid entrypoint: malformed library - procedure exported, but digest has \
102                     no node in the forest",
103                )
104            })?;
105
106            Ok(Self {
107                name: self.name.clone(),
108                version: self.version.clone(),
109                description: self.description.clone(),
110                mast: MastArtifact::Executable(Arc::new(Program::new(
111                    library.mast_forest().clone(),
112                    node_id,
113                ))),
114                manifest: PackageManifest::new(
115                    self.manifest.get_exports_by_digest(&digest).cloned(),
116                )
117                .with_dependencies(self.manifest.dependencies().cloned()),
118                sections: self.sections.clone(),
119            })
120        } else {
121            Err(Report::msg(format!(
122                "invalid entrypoint: library does not export '{entrypoint}'"
123            )))
124        }
125    }
126}