macrotest/
dependencies.rs

1use crate::error::Error;
2use crate::manifest::Edition;
3use serde::de::value::MapAccessDeserializer;
4use serde::de::{self, Deserialize, Deserializer, Visitor};
5use serde::ser::{Serialize, Serializer};
6use serde_derive::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::BTreeMap as Map;
9use std::fmt;
10use std::fs;
11use std::path::Path;
12use std::path::PathBuf;
13
14pub(crate) fn get_manifest(manifest_dir: &Path) -> Manifest {
15    try_get_manifest(manifest_dir).unwrap_or_default()
16}
17
18fn try_get_manifest(manifest_dir: &Path) -> Result<Manifest, Error> {
19    let cargo_toml_path = manifest_dir.join("Cargo.toml");
20    let manifest_str = fs::read_to_string(cargo_toml_path)?;
21    let mut manifest: Manifest = toml::de::from_str(&manifest_str)?;
22
23    fix_dependencies(&mut manifest.dependencies, manifest_dir);
24    fix_dependencies(&mut manifest.dev_dependencies, manifest_dir);
25
26    Ok(manifest)
27}
28
29pub(crate) fn get_workspace_manifest(manifest_dir: &Path) -> WorkspaceManifest {
30    try_get_workspace_manifest(manifest_dir).unwrap_or_default()
31}
32
33pub(crate) fn try_get_workspace_manifest(manifest_dir: &Path) -> Result<WorkspaceManifest, Error> {
34    let cargo_toml_path = manifest_dir.join("Cargo.toml");
35    let manifest_str = fs::read_to_string(cargo_toml_path)?;
36    let mut manifest: WorkspaceManifest = toml::de::from_str(&manifest_str)?;
37
38    fix_dependencies(&mut manifest.workspace.dependencies, manifest_dir);
39    fix_patches(&mut manifest.patch, manifest_dir);
40    fix_replacements(&mut manifest.replace, manifest_dir);
41
42    Ok(manifest)
43}
44
45fn fix_dependencies(dependencies: &mut Map<String, Dependency>, dir: &Path) {
46    dependencies.remove("macrotest");
47    for dep in dependencies.values_mut() {
48        dep.path = dep.path.as_ref().map(|path| dir.join(path));
49    }
50}
51
52fn fix_patches(patches: &mut Map<String, RegistryPatch>, dir: &Path) {
53    for registry in patches.values_mut() {
54        registry.crates.remove("macrotest");
55        for patch in registry.crates.values_mut() {
56            patch.path = patch.path.as_ref().map(|path| dir.join(path));
57        }
58    }
59}
60
61fn fix_replacements(replacements: &mut Map<String, Patch>, dir: &Path) {
62    replacements.remove("macrotest");
63    for replacement in replacements.values_mut() {
64        replacement.path = replacement.path.as_ref().map(|path| dir.join(path));
65    }
66}
67
68#[derive(Deserialize, Default, Debug)]
69pub struct WorkspaceManifest {
70    #[serde(default)]
71    pub workspace: Workspace,
72    #[serde(default)]
73    pub patch: Map<String, RegistryPatch>,
74    #[serde(default)]
75    pub replace: Map<String, Patch>,
76}
77
78#[derive(Deserialize, Default, Debug)]
79pub struct Workspace {
80    #[serde(default)]
81    pub package: WorkspacePackage,
82    #[serde(default)]
83    pub dependencies: Map<String, Dependency>,
84}
85
86#[derive(Deserialize, Default, Debug)]
87pub struct WorkspacePackage {
88    pub edition: Option<String>,
89}
90
91#[derive(Deserialize, Default, Debug)]
92pub struct Manifest {
93    #[serde(default, rename = "cargo-features")]
94    pub cargo_features: Vec<String>,
95    #[serde(default)]
96    pub package: Package,
97    #[serde(default)]
98    pub features: Map<String, Vec<String>>,
99    #[serde(default)]
100    pub dependencies: Map<String, Dependency>,
101    #[serde(default, alias = "dev-dependencies")]
102    pub dev_dependencies: Map<String, Dependency>,
103}
104
105#[derive(Deserialize, Default, Debug)]
106pub struct Package {
107    #[serde(default)]
108    pub edition: Edition,
109}
110
111#[derive(Serialize, Deserialize, Clone, Debug)]
112#[serde(remote = "Self")]
113pub struct Dependency {
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub version: Option<String>,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub path: Option<PathBuf>,
118    #[serde(
119        rename = "default-features",
120        default = "get_true",
121        skip_serializing_if = "is_true"
122    )]
123    pub default_features: bool,
124    #[serde(default, skip_serializing_if = "Vec::is_empty")]
125    pub features: Vec<String>,
126    #[serde(default, skip_serializing_if = "is_false")]
127    pub workspace: bool,
128    #[serde(flatten)]
129    pub rest: Map<String, Value>,
130}
131
132#[derive(Serialize, Deserialize, Clone, Debug)]
133#[serde(transparent)]
134pub struct RegistryPatch {
135    crates: Map<String, Patch>,
136}
137
138#[derive(Serialize, Deserialize, Clone, Debug)]
139pub struct Patch {
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub path: Option<PathBuf>,
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}
147
148fn get_true() -> bool {
149    true
150}
151
152#[allow(clippy::trivially_copy_pass_by_ref)]
153fn is_true(boolean: &bool) -> bool {
154    *boolean
155}
156
157#[allow(clippy::trivially_copy_pass_by_ref)]
158fn is_false(boolean: &bool) -> bool {
159    !*boolean
160}
161
162impl Serialize for Dependency {
163    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164    where
165        S: Serializer,
166    {
167        Dependency::serialize(self, serializer)
168    }
169}
170
171impl<'de> Deserialize<'de> for Dependency {
172    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173    where
174        D: Deserializer<'de>,
175    {
176        struct DependencyVisitor;
177
178        impl<'de> Visitor<'de> for DependencyVisitor {
179            type Value = Dependency;
180
181            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
182                formatter.write_str(
183                    "a version string like \"0.9.8\" or a \
184                     dependency like { version = \"0.9.8\" }",
185                )
186            }
187
188            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
189            where
190                E: de::Error,
191            {
192                Ok(Dependency {
193                    version: Some(s.to_owned()),
194                    path: None,
195                    default_features: true,
196                    features: Vec::new(),
197                    workspace: false,
198                    rest: Map::new(),
199                })
200            }
201
202            fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
203            where
204                M: de::MapAccess<'de>,
205            {
206                Dependency::deserialize(MapAccessDeserializer::new(map))
207            }
208        }
209
210        deserializer.deserialize_any(DependencyVisitor)
211    }
212}