use super::{
parsing::{MaybeInherit, SetSourceId, Validate},
*,
};
use crate::{SourceId, Span, Uri};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct WorkspaceTable {
#[cfg_attr(feature = "serde", serde(default))]
pub members: Vec<Span<Uri>>,
#[cfg_attr(feature = "serde", serde(default))]
pub package: PackageDetail,
#[cfg_attr(feature = "serde", serde(flatten, default))]
pub config: PackageConfig,
}
impl SetSourceId for WorkspaceTable {
fn set_source_id(&mut self, source_id: SourceId) {
let Self { members, package, config } = self;
members.set_source_id(source_id);
package.set_source_id(source_id);
config.set_source_id(source_id);
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct WorkspaceFile {
#[cfg_attr(feature = "serde", serde(skip, default))]
pub source_file: Option<Arc<SourceFile>>,
pub workspace: WorkspaceTable,
#[cfg_attr(
feature = "serde",
serde(
default,
rename = "profile",
with = "profile::serialization",
skip_serializing_if = "Vec::is_empty"
)
)]
pub profiles: Vec<Profile>,
}
impl WorkspaceFile {
#[cfg(feature = "serde")]
pub fn parse(source: Arc<SourceFile>) -> Result<Self, Report> {
use parsing::{SetSourceId, Validate};
let source_id = source.id();
let mut workspace = toml::from_str::<Self>(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,
})
})?;
workspace.source_file = Some(source.clone());
workspace.set_source_id(source_id);
workspace.validate(source)?;
Ok(workspace)
}
}
impl SetSourceId for WorkspaceFile {
fn set_source_id(&mut self, source_id: SourceId) {
let Self { source_file: _, workspace, profiles } = self;
workspace.set_source_id(source_id);
profiles.set_source_id(source_id);
}
}
impl Validate for WorkspaceFile {
fn validate(&self, source: Arc<SourceFile>) -> Result<(), Report> {
if let Some(span) = self.workspace.package.version.as_ref().and_then(|v| {
if matches!(v.inner(), MaybeInherit::Inherit) {
Some(v.span())
} else {
None
}
}) {
return Err(ProjectFileError::NotAWorkspace { source_file: source, span }.into());
}
if let Some(description) = self.workspace.package.description.as_ref()
&& matches!(description.inner(), MaybeInherit::Inherit)
{
return Err(ProjectFileError::NotAWorkspace {
source_file: source,
span: description.span(),
}
.into());
}
for dependency in self.workspace.config.dependencies.values() {
if dependency.inherits_workspace_version() {
let label = if dependency.version().is_none()
&& !dependency.is_git()
&& !dependency.is_path()
{
"expected 'version', 'digest', or 'path' here"
} else {
"cannot use the 'workspace' option in a workspace-level dependency spec"
};
return Err(Report::from(ProjectFileError::InvalidWorkspaceDependency {
source_file: source.clone(),
label: Label::new(dependency.span(), label),
}));
}
}
Ok(())
}
}