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}