1use super::{parsing::SetSourceId, *};
2use crate::{Linkage, SourceId, Span, Uri, VersionRequirement};
3
4#[derive(Debug, Clone)]
6#[cfg_attr(feature = "serde", derive(Serialize))]
7pub struct DependencySpec {
8 #[cfg_attr(feature = "serde", serde(default, skip))]
10 pub name: Span<Arc<str>>,
11 #[cfg_attr(
13 feature = "serde",
14 serde(rename = "version", alias = "digest", skip_serializing_if = "Option::is_none")
15 )]
16 pub version_or_digest: Option<VersionRequirement>,
17 #[cfg_attr(
19 feature = "serde",
20 serde(default, skip_serializing_if = "does_not_inherit_from_workspace")
21 )]
22 pub workspace: bool,
23 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
25 pub path: Option<Span<Uri>>,
26 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
29 pub git: Option<Span<Uri>>,
30 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
36 pub branch: Option<Span<Arc<str>>>,
37 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
43 pub rev: Option<Span<Arc<str>>>,
44 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
46 pub linkage: Option<Span<Linkage>>,
47}
48
49#[inline(always)]
50fn does_not_inherit_from_workspace(is_workspace_dependency: &bool) -> bool {
51 !(*is_workspace_dependency)
52}
53
54impl DependencySpec {
55 pub fn version(&self) -> Option<&VersionRequirement> {
57 self.version_or_digest.as_ref()
58 }
59
60 pub fn inherits_workspace_version(&self) -> bool {
62 self.workspace
63 }
64
65 pub fn is_host_resolved(&self) -> bool {
67 self.git.is_none() && self.path.is_none()
68 }
69
70 pub fn is_path(&self) -> bool {
72 self.path.is_some() && self.git.is_none()
73 }
74
75 pub fn is_git(&self) -> bool {
77 self.git.is_some()
78 }
79}
80
81impl SetSourceId for DependencySpec {
82 fn set_source_id(&mut self, source_id: SourceId) {
83 self.name.set_source_id(source_id);
84 if let Some(version_or_digest) = self.version_or_digest.as_mut() {
85 version_or_digest.set_source_id(source_id);
86 }
87
88 if let Some(path) = self.path.as_mut() {
89 path.set_source_id(source_id);
90 }
91
92 if let Some(git) = self.git.as_mut() {
93 git.set_source_id(source_id);
94 }
95
96 if let Some(branch) = self.branch.as_mut() {
97 branch.set_source_id(source_id);
98 }
99
100 if let Some(rev) = self.rev.as_mut() {
101 rev.set_source_id(source_id);
102 }
103 }
104}
105
106#[cfg(feature = "serde")]
107pub use self::serialization::deserialize_dependency_map;
108
109#[cfg(feature = "serde")]
110mod serialization {
111 use alloc::sync::Arc;
112
113 use miden_assembly_syntax::debuginfo::Span;
114 use serde::{
115 Deserialize,
116 de::{IntoDeserializer, MapAccess, Visitor},
117 };
118
119 use super::DependencySpec;
120 use crate::Map;
121
122 #[derive(Deserialize)]
123 #[serde(deny_unknown_fields)]
124 struct DependencySpecTable {
125 #[serde(default, skip)]
126 name: Span<Arc<str>>,
127 #[serde(rename = "version", alias = "digest", skip_serializing_if = "Option::is_none")]
128 version_or_digest: Option<crate::VersionRequirement>,
129 #[serde(default, skip_serializing_if = "super::does_not_inherit_from_workspace")]
130 workspace: bool,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
132 path: Option<Span<crate::Uri>>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 git: Option<Span<crate::Uri>>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
136 branch: Option<Span<Arc<str>>>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
138 rev: Option<Span<Arc<str>>>,
139 #[serde(default, skip_serializing_if = "Option::is_none")]
140 linkage: Option<Span<crate::Linkage>>,
141 }
142
143 impl From<DependencySpecTable> for DependencySpec {
144 fn from(table: DependencySpecTable) -> Self {
145 Self {
146 name: table.name,
147 version_or_digest: table.version_or_digest,
148 workspace: table.workspace,
149 path: table.path,
150 git: table.git,
151 branch: table.branch,
152 rev: table.rev,
153 linkage: table.linkage,
154 }
155 }
156 }
157
158 impl<'de> Deserialize<'de> for DependencySpec {
159 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160 where
161 D: serde::Deserializer<'de>,
162 {
163 serde_untagged::UntaggedEnumVisitor::new()
164 .expecting("a dependency requirement string or dependency table")
165 .string(|value| {
166 crate::VersionRequirement::deserialize(value.into_deserializer()).map(
167 |version_or_digest| Self {
168 name: Span::default(),
169 version_or_digest: Some(version_or_digest),
170 workspace: false,
171 path: None,
172 git: None,
173 branch: None,
174 rev: None,
175 linkage: None,
176 },
177 )
178 })
179 .map(|map| map.deserialize::<DependencySpecTable>().map(Into::into))
180 .deserialize(deserializer)
181 }
182 }
183
184 struct DependencyMapVisitor;
185
186 impl<'de> Visitor<'de> for DependencyMapVisitor {
187 type Value = Map<Span<Arc<str>>, Span<DependencySpec>>;
188
189 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
190 formatter.write_str("a dependency map")
191 }
192
193 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
194 where
195 M: MapAccess<'de>,
196 {
197 let mut map = Self::Value::default();
198
199 while let Some((key, mut value)) =
200 access.next_entry::<Span<Arc<str>>, Span<DependencySpec>>()?
201 {
202 value.name = key.clone();
203 map.insert(key, value);
204 }
205
206 Ok(map)
207 }
208 }
209
210 #[allow(clippy::type_complexity)]
211 pub fn deserialize_dependency_map<'de, D>(
212 deserializer: D,
213 ) -> Result<Map<Span<Arc<str>>, Span<DependencySpec>>, D::Error>
214 where
215 D: serde::Deserializer<'de>,
216 {
217 deserializer.deserialize_map(DependencyMapVisitor)
218 }
219}