use super::{parsing::SetSourceId, *};
use crate::{Linkage, SourceId, Span, Uri, VersionRequirement};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DependencySpec {
#[cfg_attr(feature = "serde", serde(default, skip))]
pub name: Span<Arc<str>>,
#[cfg_attr(
feature = "serde",
serde(rename = "version", alias = "digest", skip_serializing_if = "Option::is_none")
)]
pub version_or_digest: Option<VersionRequirement>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "does_not_inherit_from_workspace")
)]
pub workspace: bool,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub path: Option<Span<Uri>>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub git: Option<Span<Uri>>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub branch: Option<Span<Arc<str>>>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub rev: Option<Span<Arc<str>>>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub linkage: Option<Span<Linkage>>,
}
#[inline(always)]
fn does_not_inherit_from_workspace(is_workspace_dependency: &bool) -> bool {
!(*is_workspace_dependency)
}
impl DependencySpec {
pub fn version(&self) -> Option<&VersionRequirement> {
self.version_or_digest.as_ref()
}
pub fn inherits_workspace_version(&self) -> bool {
self.workspace
}
pub fn is_host_resolved(&self) -> bool {
self.git.is_none() && self.path.is_none()
}
pub fn is_path(&self) -> bool {
self.path.is_some() && self.git.is_none()
}
pub fn is_git(&self) -> bool {
self.git.is_some()
}
}
impl SetSourceId for DependencySpec {
fn set_source_id(&mut self, source_id: SourceId) {
self.name.set_source_id(source_id);
if let Some(version_or_digest) = self.version_or_digest.as_mut() {
version_or_digest.set_source_id(source_id);
}
if let Some(path) = self.path.as_mut() {
path.set_source_id(source_id);
}
if let Some(git) = self.git.as_mut() {
git.set_source_id(source_id);
}
if let Some(branch) = self.branch.as_mut() {
branch.set_source_id(source_id);
}
if let Some(rev) = self.rev.as_mut() {
rev.set_source_id(source_id);
}
}
}
#[cfg(feature = "serde")]
pub use self::serialization::deserialize_dependency_map;
#[cfg(feature = "serde")]
mod serialization {
use alloc::sync::Arc;
use miden_assembly_syntax::debuginfo::Span;
use serde::{
Deserialize,
de::{IntoDeserializer, MapAccess, Visitor},
};
use super::DependencySpec;
use crate::Map;
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct DependencySpecTable {
#[serde(default, skip)]
name: Span<Arc<str>>,
#[serde(rename = "version", alias = "digest", skip_serializing_if = "Option::is_none")]
version_or_digest: Option<crate::VersionRequirement>,
#[serde(default, skip_serializing_if = "super::does_not_inherit_from_workspace")]
workspace: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
path: Option<Span<crate::Uri>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
git: Option<Span<crate::Uri>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
branch: Option<Span<Arc<str>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
rev: Option<Span<Arc<str>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
linkage: Option<Span<crate::Linkage>>,
}
impl From<DependencySpecTable> for DependencySpec {
fn from(table: DependencySpecTable) -> Self {
Self {
name: table.name,
version_or_digest: table.version_or_digest,
workspace: table.workspace,
path: table.path,
git: table.git,
branch: table.branch,
rev: table.rev,
linkage: table.linkage,
}
}
}
impl<'de> Deserialize<'de> for DependencySpec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
serde_untagged::UntaggedEnumVisitor::new()
.expecting("a dependency requirement string or dependency table")
.string(|value| {
crate::VersionRequirement::deserialize(value.into_deserializer()).map(
|version_or_digest| Self {
name: Span::default(),
version_or_digest: Some(version_or_digest),
workspace: false,
path: None,
git: None,
branch: None,
rev: None,
linkage: None,
},
)
})
.map(|map| map.deserialize::<DependencySpecTable>().map(Into::into))
.deserialize(deserializer)
}
}
struct DependencyMapVisitor;
impl<'de> Visitor<'de> for DependencyMapVisitor {
type Value = Map<Span<Arc<str>>, Span<DependencySpec>>;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("a dependency map")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = Self::Value::default();
while let Some((key, mut value)) =
access.next_entry::<Span<Arc<str>>, Span<DependencySpec>>()?
{
value.name = key.clone();
map.insert(key, value);
}
Ok(map)
}
}
#[allow(clippy::type_complexity)]
pub fn deserialize_dependency_map<'de, D>(
deserializer: D,
) -> Result<Map<Span<Arc<str>>, Span<DependencySpec>>, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_map(DependencyMapVisitor)
}
}