mod dependency;
mod package;
pub(crate) mod parsing;
mod profile;
mod target;
#[cfg(all(test, feature = "std", feature = "serde"))]
mod tests;
mod workspace;
use alloc::{
boxed::Box,
format,
string::{String, ToString},
sync::Arc,
vec,
vec::Vec,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub use self::{
dependency::DependencySpec,
package::{PackageConfig, PackageDetail, PackageTable, ProjectFile},
profile::Profile,
target::{BinTarget, LibTarget},
workspace::WorkspaceFile,
};
use crate::{Diagnostic, Label, RelatedError, Report, SourceFile, SourceSpan, miette};
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged, rename_all = "lowercase"))]
pub enum MidenProject {
Workspace(Box<WorkspaceFile>),
Package(Box<ProjectFile>),
}
impl MidenProject {
pub fn is_workspace(&self) -> bool {
matches!(self, Self::Workspace(_))
}
}
#[cfg(feature = "serde")]
impl MidenProject {
pub fn parse(source: Arc<SourceFile>) -> Result<Self, Report> {
let toml = toml::from_str::<toml::Table>(source.as_str()).map_err(|err| {
let span = err
.span()
.map(|span| {
let start = span.start as u32;
let end = span.end as u32;
SourceSpan::new(source.id(), start..end)
})
.unwrap_or_default();
Report::from(ProjectFileError::ParseError {
message: err.message().to_string(),
source_file: source.clone(),
span,
})
})?;
if toml.contains_key("workspace") {
Ok(Self::Workspace(Box::new(WorkspaceFile::parse(source)?)))
} else {
Ok(Self::Package(Box::new(ProjectFile::parse(source)?)))
}
}
}
#[allow(dead_code)] #[derive(Debug, thiserror::Error, Diagnostic)]
pub(crate) enum ProjectFileError {
#[error("unable to parse project manifest: {message}")]
ParseError {
message: String,
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
span: SourceSpan,
},
#[error("invalid project name")]
#[diagnostic(help("The project name must be a valid Miden Assembly namespace identifier"))]
InvalidProjectName {
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
label: Label,
},
#[error("invalid workspace dependency specification")]
InvalidWorkspaceDependency {
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
label: Label,
},
#[error("invalid dependency specification: {}", label.label().unwrap_or(""))]
InvalidPackageDependency {
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
label: Label,
},
#[error("invalid build target configuration")]
InvalidBuildTargets {
#[source_code]
source_file: Arc<SourceFile>,
#[related]
related: Vec<RelatedError>,
},
#[error("package is not a member of a workspace")]
NotAWorkspace {
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
span: SourceSpan,
},
#[error("failed to load workspace member: {}", span.label().unwrap_or("unknown"))]
LoadWorkspaceMemberFailed {
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
span: Label,
},
#[error("duplicate workspace member package name '{name}'")]
DuplicateWorkspaceMember {
name: String,
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary, "duplicate workspace member")]
span: SourceSpan,
#[label("previous workspace member")]
prev: SourceSpan,
},
#[error("no profile named '{name}' has been defined yet")]
UnknownProfile {
name: Arc<str>,
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
span: SourceSpan,
},
#[error("cannot redefine profile '{name}'")]
DuplicateProfile {
name: Arc<str>,
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
span: SourceSpan,
#[label]
prev: SourceSpan,
},
#[error("missing required field 'version'")]
MissingVersion {
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
span: SourceSpan,
},
#[error("workspace does not define 'version'")]
MissingWorkspaceVersion {
#[source_code]
source_file: Arc<SourceFile>,
#[label(primary)]
span: SourceSpan,
},
}