Skip to main content

miden_mast_package/package/
mod.rs

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