1mod dependency;
8mod package;
9pub(crate) mod parsing;
10mod profile;
11mod target;
12#[cfg(all(test, feature = "std", feature = "serde"))]
13mod tests;
14mod workspace;
15
16use alloc::{
17 boxed::Box,
18 format,
19 string::{String, ToString},
20 sync::Arc,
21 vec,
22 vec::Vec,
23};
24
25#[cfg(feature = "serde")]
26use serde::{Deserialize, Serialize};
27
28pub use self::{
29 dependency::DependencySpec,
30 package::{PackageConfig, PackageDetail, ProjectFile},
31 profile::Profile,
32 target::{BinTarget, LibTarget},
33 workspace::WorkspaceFile,
34};
35use crate::{Diagnostic, Label, RelatedError, Report, SourceFile, SourceSpan, miette};
36
37#[derive(Debug)]
39#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40#[cfg_attr(feature = "serde", serde(untagged, rename_all = "lowercase"))]
41pub enum MidenProject {
42 Workspace(Box<WorkspaceFile>),
47 Package(Box<ProjectFile>),
52}
53
54impl MidenProject {
56 pub fn is_workspace(&self) -> bool {
58 matches!(self, Self::Workspace(_))
59 }
60}
61
62#[cfg(feature = "serde")]
64impl MidenProject {
65 pub fn parse(source: Arc<SourceFile>) -> Result<Self, Report> {
77 let toml = toml::from_str::<toml::Table>(source.as_str()).map_err(|err| {
81 let span = err
82 .span()
83 .map(|span| {
84 let start = span.start as u32;
85 let end = span.end as u32;
86 SourceSpan::new(source.id(), start..end)
87 })
88 .unwrap_or_default();
89 Report::from(ProjectFileError::ParseError {
90 message: err.message().to_string(),
91 source_file: source.clone(),
92 span,
93 })
94 })?;
95 if toml.contains_key("workspace") {
96 Ok(Self::Workspace(Box::new(WorkspaceFile::parse(source)?)))
97 } else {
98 Ok(Self::Package(Box::new(ProjectFile::parse(source)?)))
99 }
100 }
101}
102
103#[allow(dead_code)] #[derive(Debug, thiserror::Error, Diagnostic)]
106pub(crate) enum ProjectFileError {
107 #[error("unable to parse project manifest: {message}")]
108 ParseError {
109 message: String,
110 #[source_code]
111 source_file: Arc<SourceFile>,
112 #[label(primary)]
113 span: SourceSpan,
114 },
115 #[error("invalid project name")]
116 #[diagnostic(help("The project name must be a valid Miden Assembly namespace identifier"))]
117 InvalidProjectName {
118 #[source_code]
119 source_file: Arc<SourceFile>,
120 #[label(primary)]
121 label: Label,
122 },
123 #[error("invalid workspace dependency specification")]
124 InvalidWorkspaceDependency {
125 #[source_code]
126 source_file: Arc<SourceFile>,
127 #[label(primary)]
128 label: Label,
129 },
130 #[error("invalid dependency specification")]
131 InvalidPackageDependency {
132 #[source_code]
133 source_file: Arc<SourceFile>,
134 #[label(primary)]
135 label: Label,
136 },
137 #[error("invalid build target configuration")]
138 InvalidBuildTargets {
139 #[source_code]
140 source_file: Arc<SourceFile>,
141 #[related]
142 related: Vec<RelatedError>,
143 },
144 #[error("package is not a member of a workspace")]
145 NotAWorkspace {
146 #[source_code]
147 source_file: Arc<SourceFile>,
148 #[label(primary)]
149 span: SourceSpan,
150 },
151 #[error("failed to load workspace member: {}", span.label().unwrap_or("unknown"))]
152 LoadWorkspaceMemberFailed {
153 #[source_code]
154 source_file: Arc<SourceFile>,
155 #[label(primary)]
156 span: Label,
157 },
158 #[error("no profile named '{name}' has been defined yet")]
159 UnknownProfile {
160 name: Arc<str>,
161 #[source_code]
162 source_file: Arc<SourceFile>,
163 #[label(primary)]
164 span: SourceSpan,
165 },
166 #[error("cannot redefine profile '{name}'")]
167 DuplicateProfile {
168 name: Arc<str>,
169 #[source_code]
170 source_file: Arc<SourceFile>,
171 #[label(primary)]
172 span: SourceSpan,
173 #[label]
174 prev: SourceSpan,
175 },
176 #[error("missing required field 'version'")]
177 MissingVersion {
178 #[source_code]
179 source_file: Arc<SourceFile>,
180 #[label(primary)]
181 span: SourceSpan,
182 },
183 #[error("workspace does not define 'version'")]
184 MissingWorkspaceVersion {
185 #[source_code]
186 source_file: Arc<SourceFile>,
187 #[label(primary)]
188 span: SourceSpan,
189 },
190}