#![no_std]
#[macro_use]
extern crate alloc;
#[cfg(any(test, feature = "std"))]
extern crate std;
#[cfg(feature = "resolver")]
mod resolver;
mod version;
mod version_requirement;
use alloc::{collections::BTreeMap, string::String, sync::Arc};
use core::fmt;
use miden_assembly_syntax::Report;
pub use miden_assembly_syntax::{
debuginfo::Span,
semver,
semver::{Version as SemVer, VersionReq},
};
pub use miden_core::{LexicographicWord, Word};
use miden_mast_package::Package as MastPackage;
pub use miden_mast_package::PackageId;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "resolver")]
pub use self::resolver::{
DependencyResolutionError, InMemoryPackageRegistry, PackagePriority, PackageResolver,
VersionSet,
};
pub use self::{
version::{InvalidVersionError, SemVerError, Version},
version_requirement::VersionRequirement,
};
pub type PackageRequirements = BTreeMap<PackageId, VersionRequirement>;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PackageRecord {
version: Version,
description: Option<Arc<str>>,
dependencies: PackageRequirements,
}
impl PackageRecord {
pub fn new(
version: Version,
dependencies: impl IntoIterator<Item = (PackageId, VersionRequirement)>,
) -> Self {
Self {
version,
description: None,
dependencies: dependencies.into_iter().collect(),
}
}
pub fn with_description(mut self, description: impl Into<Arc<str>>) -> Self {
self.description = Some(description.into());
self
}
pub fn version(&self) -> &Version {
&self.version
}
pub fn semantic_version(&self) -> &SemVer {
&self.version.version
}
pub fn digest(&self) -> Option<&Word> {
self.version.digest.as_ref().map(|word| word.inner())
}
pub fn description(&self) -> Option<&Arc<str>> {
self.description.as_ref()
}
pub fn dependencies(&self) -> &PackageRequirements {
&self.dependencies
}
}
pub type PackageVersions = BTreeMap<SemVer, PackageRecord>;
pub trait PackageRegistry {
fn available_versions(&self, package: &PackageId) -> Option<&PackageVersions>;
fn is_available(&self, package: &PackageId) -> bool {
self.available_versions(package).is_some()
}
fn is_version_available(&self, package: &PackageId, version: &Version) -> bool {
self.get_by_version(package, version).is_some()
}
fn is_semver_available(&self, package: &PackageId, version: &SemVer) -> bool {
self.get_by_semver(package, version).is_some()
}
fn get_by_version(&self, package: &PackageId, version: &Version) -> Option<&PackageRecord> {
let record = self.available_versions(package)?.get(&version.version)?;
match version.digest.as_ref() {
Some(_) if record.version() == version => Some(record),
Some(_) => None,
None => Some(record),
}
}
fn get_by_semver(&self, package: &PackageId, version: &SemVer) -> Option<&PackageRecord> {
self.available_versions(package)?.get(version)
}
fn get_exact_version(&self, package: &PackageId, version: &Version) -> Option<&PackageRecord> {
match version.digest.as_ref() {
Some(_) => self.get_by_version(package, version),
None => None,
}
}
fn get_by_digest(&self, package: &PackageId, digest: &Word) -> Option<&PackageRecord> {
let digest = LexicographicWord::new(*digest);
self.available_versions(package).and_then(|versions| {
versions
.values()
.rev()
.find(|record| record.version().digest.is_some_and(|d| d == digest))
})
}
fn find_latest<'a>(
&'a self,
package: &PackageId,
requirement: &VersionRequirement,
) -> Option<&'a PackageRecord> {
if let VersionRequirement::Exact(version) = requirement {
return self.get_exact_version(package, version);
}
self.available_versions(package).and_then(|versions| {
versions.values().rev().find(|record| record.version().satisfies(requirement))
})
}
}
pub trait PackageProvider {
fn load_package(
&self,
package: &PackageId,
version: &Version,
) -> Result<Arc<MastPackage>, Report>;
}
pub trait PackageIndex: PackageRegistry {
type Error: fmt::Display;
fn register(&mut self, name: PackageId, record: PackageRecord) -> Result<(), Self::Error>;
}
pub trait PackageStore: PackageRegistry + PackageProvider {
type Error: fmt::Display;
fn publish_package(&mut self, package: Arc<MastPackage>) -> Result<Version, Self::Error>;
}
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
pub struct NoPackageStoreError(String);
#[derive(Default)]
pub struct NoPackageStore;
impl PackageRegistry for NoPackageStore {
fn available_versions(&self, _package: &PackageId) -> Option<&PackageVersions> {
None
}
}
impl PackageProvider for NoPackageStore {
fn load_package(
&self,
package: &PackageId,
version: &Version,
) -> Result<Arc<MastPackage>, Report> {
Err(Report::msg(format!("cannot load package {package}@{version}")))
}
}
impl PackageStore for NoPackageStore {
type Error = NoPackageStoreError;
fn publish_package(&mut self, package: Arc<MastPackage>) -> Result<Version, Self::Error> {
Err(NoPackageStoreError(format!(
"cannot publish package {}@{}",
package.name, package.version
)))
}
}