trybuild_internals_api/
dependencies.rs

1use crate::directory::Directory;
2use crate::error::Error;
3use crate::inherit::InheritEdition;
4use crate::manifest::Edition;
5use serde::de::value::MapAccessDeserializer;
6use serde::de::value::StrDeserializer;
7use serde::de::{self, Deserialize, Deserializer, Visitor};
8use serde::ser::{Serialize, Serializer};
9use serde_derive::{Deserialize, Serialize};
10use serde_json::Value;
11use std::collections::BTreeMap as Map;
12use std::fmt;
13use std::fs;
14use std::path::PathBuf;
15
16pub fn get_manifest(manifest_dir: &Directory) -> Result<Manifest, Error> {
17    let cargo_toml_path = manifest_dir.join("Cargo.toml");
18    let mut manifest = (|| {
19        let manifest_str = fs::read_to_string(&cargo_toml_path)?;
20        let manifest: Manifest = toml::from_str(&manifest_str)?;
21        Ok(manifest)
22    })()
23    .map_err(|err| Error::GetManifest(cargo_toml_path, Box::new(err)))?;
24
25    fix_dependencies(&mut manifest.dependencies, manifest_dir);
26    fix_dependencies(&mut manifest.dev_dependencies, manifest_dir);
27    for target in manifest.target.values_mut() {
28        fix_dependencies(&mut target.dependencies, manifest_dir);
29        fix_dependencies(&mut target.dev_dependencies, manifest_dir);
30    }
31
32    Ok(manifest)
33}
34
35pub fn get_workspace_manifest(manifest_dir: &Directory) -> WorkspaceManifest {
36    try_get_workspace_manifest(manifest_dir).unwrap_or_default()
37}
38
39pub fn try_get_workspace_manifest(
40    manifest_dir: &Directory,
41) -> Result<WorkspaceManifest, Error> {
42    let cargo_toml_path = manifest_dir.join("Cargo.toml");
43    let manifest_str = fs::read_to_string(cargo_toml_path)?;
44    let mut manifest: WorkspaceManifest = toml::from_str(&manifest_str)?;
45
46    fix_dependencies(&mut manifest.workspace.dependencies, manifest_dir);
47    fix_patches(&mut manifest.patch, manifest_dir);
48    fix_replacements(&mut manifest.replace, manifest_dir);
49
50    Ok(manifest)
51}
52
53fn fix_dependencies(dependencies: &mut Map<String, Dependency>, dir: &Directory) {
54    dependencies.remove("trybuild");
55    for dep in dependencies.values_mut() {
56        dep.path = dep.path.as_ref().map(|path| Directory::new(dir.join(path)));
57    }
58}
59
60fn fix_patches(patches: &mut Map<String, RegistryPatch>, dir: &Directory) {
61    for registry in patches.values_mut() {
62        registry.crates.remove("trybuild");
63        for patch in registry.crates.values_mut() {
64            patch.path = patch.path.as_ref().map(|path| dir.join(path));
65        }
66    }
67}
68
69fn fix_replacements(replacements: &mut Map<String, Patch>, dir: &Directory) {
70    replacements.remove("trybuild");
71    for replacement in replacements.values_mut() {
72        replacement.path = replacement.path.as_ref().map(|path| dir.join(path));
73    }
74}
75
76#[derive(Deserialize, Default, Debug)]
77pub struct WorkspaceManifest {
78    #[serde(default)]
79    pub workspace: WorkspaceWorkspace,
80    #[serde(default)]
81    pub patch: Map<String, RegistryPatch>,
82    #[serde(default)]
83    pub replace: Map<String, Patch>,
84}
85
86#[derive(Deserialize, Default, Debug)]
87pub struct WorkspaceWorkspace {
88    #[serde(default)]
89    pub package: WorkspacePackage,
90    #[serde(default)]
91    pub dependencies: Map<String, Dependency>,
92}
93
94#[derive(Deserialize, Default, Debug)]
95pub struct WorkspacePackage {
96    pub edition: Option<Edition>,
97}
98
99#[derive(Deserialize, Default, Debug)]
100pub struct Manifest {
101    #[serde(rename = "cargo-features", default)]
102    pub cargo_features: Vec<String>,
103    #[serde(default)]
104    pub package: Package,
105    #[serde(default)]
106    pub features: Map<String, Vec<String>>,
107    #[serde(default)]
108    pub dependencies: Map<String, Dependency>,
109    #[serde(default, alias = "dev-dependencies")]
110    pub dev_dependencies: Map<String, Dependency>,
111    #[serde(default)]
112    pub target: Map<String, TargetDependencies>,
113}
114
115#[derive(Deserialize, Default, Debug)]
116pub struct Package {
117    pub name: String,
118    #[serde(default)]
119    pub edition: EditionOrInherit,
120    pub resolver: Option<String>,
121}
122
123#[derive(Debug)]
124pub enum EditionOrInherit {
125    Edition(Edition),
126    Inherit,
127}
128
129#[derive(Serialize, Deserialize, Clone, Debug)]
130#[serde(remote = "Self")]
131pub struct Dependency {
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub version: Option<String>,
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub path: Option<Directory>,
136    #[serde(default, skip_serializing_if = "is_false")]
137    pub optional: bool,
138    #[serde(rename = "default-features", skip_serializing_if = "Option::is_none")]
139    pub default_features: Option<bool>,
140    #[serde(default, skip_serializing_if = "Vec::is_empty")]
141    pub features: Vec<String>,
142    #[serde(skip_serializing_if = "Option::is_none")]
143    pub git: Option<String>,
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub branch: Option<String>,
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub tag: Option<String>,
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub rev: Option<String>,
150    #[serde(default, skip_serializing_if = "is_false")]
151    pub workspace: bool,
152    #[serde(flatten)]
153    pub rest: Map<String, Value>,
154}
155
156#[derive(Serialize, Deserialize, Clone, Debug)]
157pub struct TargetDependencies {
158    #[serde(default, skip_serializing_if = "Map::is_empty")]
159    pub dependencies: Map<String, Dependency>,
160    #[serde(
161        default,
162        alias = "dev-dependencies",
163        skip_serializing_if = "Map::is_empty"
164    )]
165    pub dev_dependencies: Map<String, Dependency>,
166}
167
168#[derive(Serialize, Deserialize, Clone, Debug)]
169#[serde(transparent)]
170pub struct RegistryPatch {
171    pub crates: Map<String, Patch>,
172}
173
174#[derive(Serialize, Deserialize, Clone, Debug)]
175pub struct Patch {
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub path: Option<PathBuf>,
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub git: Option<String>,
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub branch: Option<String>,
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub tag: Option<String>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub rev: Option<String>,
186    #[serde(flatten)]
187    pub rest: Map<String, Value>,
188}
189
190fn is_false(boolean: &bool) -> bool {
191    !*boolean
192}
193
194impl Default for EditionOrInherit {
195    fn default() -> Self {
196        EditionOrInherit::Edition(Edition::default())
197    }
198}
199
200impl<'de> Deserialize<'de> for EditionOrInherit {
201    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
202    where
203        D: Deserializer<'de>,
204    {
205        struct EditionOrInheritVisitor;
206
207        impl<'de> Visitor<'de> for EditionOrInheritVisitor {
208            type Value = EditionOrInherit;
209
210            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
211                formatter.write_str("edition")
212            }
213
214            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
215            where
216                E: de::Error,
217            {
218                Edition::deserialize(StrDeserializer::new(s)).map(EditionOrInherit::Edition)
219            }
220
221            fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
222            where
223                M: de::MapAccess<'de>,
224            {
225                InheritEdition::deserialize(MapAccessDeserializer::new(map))?;
226                Ok(EditionOrInherit::Inherit)
227            }
228        }
229
230        deserializer.deserialize_any(EditionOrInheritVisitor)
231    }
232}
233
234impl Serialize for Dependency {
235    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
236    where
237        S: Serializer,
238    {
239        Dependency::serialize(self, serializer)
240    }
241}
242
243impl<'de> Deserialize<'de> for Dependency {
244    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245    where
246        D: Deserializer<'de>,
247    {
248        struct DependencyVisitor;
249
250        impl<'de> Visitor<'de> for DependencyVisitor {
251            type Value = Dependency;
252
253            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
254                formatter.write_str(
255                    "a version string like \"0.9.8\" or a \
256                     dependency like { version = \"0.9.8\" }",
257                )
258            }
259
260            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
261            where
262                E: de::Error,
263            {
264                Ok(Dependency {
265                    version: Some(s.to_owned()),
266                    path: None,
267                    optional: false,
268                    default_features: Some(true),
269                    features: Vec::new(),
270                    git: None,
271                    branch: None,
272                    tag: None,
273                    rev: None,
274                    workspace: false,
275                    rest: Map::new(),
276                })
277            }
278
279            fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
280            where
281                M: de::MapAccess<'de>,
282            {
283                Dependency::deserialize(MapAccessDeserializer::new(map))
284            }
285        }
286
287        deserializer.deserialize_any(DependencyVisitor)
288    }
289}