miden_mast_package/package/
mod.rs

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