Skip to content

Commit a9e2c81

Browse files
committed
cargo-rail: split the manifest_ops into modular files.
1 parent 7aaaad1 commit a9e2c81

File tree

8 files changed

+1387
-1316
lines changed

8 files changed

+1387
-1316
lines changed

src/cargo/manifest_ops.rs

Lines changed: 0 additions & 1316 deletions
This file was deleted.
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
//! Dependency entry building functions
2+
3+
use crate::cargo::unify_types::UnifiedDep;
4+
use toml_edit::{InlineTable, Item, Value};
5+
6+
use super::fields::build_feature_array;
7+
8+
/// Build a dependency entry for [workspace.dependencies]
9+
///
10+
/// Returns:
11+
/// - `Item::Value(Value::String)` for simple case (version only)
12+
/// - `Item::Value(Value::InlineTable)` for complex case (features, path, etc.)
13+
///
14+
/// # Examples
15+
///
16+
/// ```ignore
17+
/// let dep = UnifiedDep {
18+
/// name: "serde".to_string(),
19+
/// version_req: "1.0".parse().unwrap(),
20+
/// features: vec![],
21+
/// default_features: true,
22+
/// used_by: vec!["crate1".to_string()],
23+
/// target: None,
24+
/// path: None,
25+
/// };
26+
/// let entry = build_dep_entry(&dep);
27+
/// // Result: Item::Value(Value::String("1.0"))
28+
/// ```
29+
pub fn build_dep_entry(dep: &UnifiedDep) -> Item {
30+
// Simple case: just version, no features, defaults enabled, no path
31+
if dep.features.is_empty() && dep.default_features && dep.path.is_none() {
32+
let mut value = Value::from(dep.version_req.to_string());
33+
value.decor_mut().set_suffix(" #unified");
34+
return Item::Value(value);
35+
}
36+
37+
// Complex case: use inline table
38+
let mut table = InlineTable::new();
39+
40+
// Add path if present (for workspace member deps)
41+
if let Some(ref path) = dep.path {
42+
table.insert("path", Value::from(path.display().to_string()));
43+
// Also include version for publishable workspace members
44+
// This allows `cargo publish` to work while using paths for local dev
45+
if dep.version_req.to_string() != "*" {
46+
table.insert("version", Value::from(dep.version_req.to_string()));
47+
}
48+
} else {
49+
table.insert("version", Value::from(dep.version_req.to_string()));
50+
}
51+
52+
// Add default-features flag if false
53+
if !dep.default_features {
54+
table.insert("default-features", Value::from(false));
55+
}
56+
57+
// Add features if any
58+
if !dep.features.is_empty() {
59+
table.insert("features", build_feature_array(&dep.features));
60+
}
61+
62+
// Add #unified comment marker
63+
let mut value = Value::InlineTable(table);
64+
value.decor_mut().set_suffix(" #unified");
65+
66+
Item::Value(value)
67+
}
68+
69+
/// Build a workspace-inherited dependency entry
70+
///
71+
/// Used in member manifests to reference [workspace.dependencies].
72+
///
73+
/// # Arguments
74+
///
75+
/// * `local_features` - Additional features to enable locally (beyond workspace features)
76+
/// * `is_optional` - Whether the dependency is optional
77+
///
78+
/// # Returns
79+
///
80+
/// `{ workspace = true }` with optional fields and `#unified` comment marker
81+
pub fn build_workspace_dep_entry(local_features: Option<Vec<String>>, is_optional: bool) -> Item {
82+
let mut table = InlineTable::new();
83+
table.insert("workspace", Value::from(true));
84+
85+
// Add local features if any
86+
if let Some(features) = local_features
87+
&& !features.is_empty()
88+
{
89+
table.insert("features", build_feature_array(&features));
90+
}
91+
92+
// Add optional if needed
93+
if is_optional {
94+
table.insert("optional", Value::from(true));
95+
}
96+
97+
// Add #unified comment marker to track what was modified by unify
98+
let mut value = Value::InlineTable(table);
99+
value.decor_mut().set_suffix(" #unified");
100+
101+
Item::Value(value)
102+
}
103+
104+
/// Build a transitive dependency entry for pinning
105+
///
106+
/// Used for workspace-hack replacement (dev-dependencies with workspace = true and features).
107+
///
108+
/// # Arguments
109+
///
110+
/// * `features` - Features to enable for the transitive dependency
111+
pub fn build_transitive_entry(features: &[String]) -> Item {
112+
let mut table = InlineTable::new();
113+
table.insert("workspace", Value::from(true));
114+
115+
if !features.is_empty() {
116+
table.insert("features", build_feature_array(features));
117+
}
118+
119+
// Add #unified comment marker
120+
let mut value = Value::InlineTable(table);
121+
value.decor_mut().set_suffix(" #unified");
122+
123+
Item::Value(value)
124+
}
125+
126+
/// Build a versioned dependency entry for [workspace.dependencies]
127+
///
128+
/// Used when adding transitive dependencies to workspace.dependencies.
129+
/// Creates entries like: `dep = { version = "1.0", features = ["foo"] }`
130+
///
131+
/// # Arguments
132+
///
133+
/// * `version` - The semver version to use
134+
/// * `features` - Features to enable
135+
pub fn build_versioned_dep_entry(version: &semver::Version, features: &[String]) -> Item {
136+
// Simple case: just version, no features
137+
if features.is_empty() {
138+
let mut value = Value::from(format!("^{}", version));
139+
value.decor_mut().set_suffix(" #unified");
140+
return Item::Value(value);
141+
}
142+
143+
// Complex case: version + features
144+
let mut table = InlineTable::new();
145+
table.insert("version", Value::from(format!("^{}", version)));
146+
table.insert("features", build_feature_array(features));
147+
148+
// Add #unified comment marker
149+
let mut value = Value::InlineTable(table);
150+
value.decor_mut().set_suffix(" #unified");
151+
152+
Item::Value(value)
153+
}
154+
155+
#[cfg(test)]
156+
mod tests {
157+
use super::*;
158+
use crate::cargo::unify_types::UnifiedDep;
159+
use std::path::PathBuf;
160+
161+
use super::super::fields::{extract_features, extract_path, has_default_features, has_path};
162+
use super::super::transform::{extract_version, is_inline_table_dep, is_optional, is_simple_string_dep};
163+
use super::super::workspace_ref::is_workspace_dep;
164+
165+
fn create_test_dep(name: &str, version: &str) -> UnifiedDep {
166+
UnifiedDep {
167+
name: name.to_string(),
168+
version_req: version.parse().unwrap(),
169+
features: vec![],
170+
default_features: true,
171+
used_by: vec!["test-crate".to_string()],
172+
target: None,
173+
path: None,
174+
}
175+
}
176+
177+
#[test]
178+
fn test_build_dep_entry_simple() {
179+
let dep = create_test_dep("serde", "1.0");
180+
let entry = build_dep_entry(&dep);
181+
assert!(is_simple_string_dep(&entry));
182+
// Version requirement parsing adds the caret operator
183+
assert_eq!(extract_version(&entry).unwrap(), "^1.0");
184+
}
185+
186+
#[test]
187+
fn test_build_dep_entry_with_features() {
188+
let mut dep = create_test_dep("serde", "1.0");
189+
dep.features = vec!["derive".to_string()];
190+
191+
let entry = build_dep_entry(&dep);
192+
assert!(is_inline_table_dep(&entry));
193+
let features = extract_features(&entry).unwrap();
194+
assert_eq!(features, vec!["derive"]);
195+
}
196+
197+
#[test]
198+
fn test_build_dep_entry_with_path() {
199+
let mut dep = create_test_dep("local-crate", "0.1.0");
200+
dep.path = Some(PathBuf::from("../local-crate"));
201+
202+
let entry = build_dep_entry(&dep);
203+
assert!(is_inline_table_dep(&entry));
204+
assert!(has_path(&entry));
205+
assert_eq!(extract_path(&entry).unwrap(), "../local-crate");
206+
}
207+
208+
#[test]
209+
fn test_build_dep_entry_no_default_features() {
210+
let mut dep = create_test_dep("tokio", "1.0");
211+
dep.default_features = false;
212+
213+
let entry = build_dep_entry(&dep);
214+
assert!(is_inline_table_dep(&entry));
215+
assert!(!has_default_features(&entry));
216+
}
217+
218+
#[test]
219+
fn test_build_workspace_dep_entry_simple() {
220+
let entry = build_workspace_dep_entry(None, false);
221+
assert!(is_workspace_dep(&entry));
222+
assert!(is_inline_table_dep(&entry));
223+
}
224+
225+
#[test]
226+
fn test_build_workspace_dep_entry_with_features() {
227+
let entry = build_workspace_dep_entry(Some(vec!["extra".to_string()]), false);
228+
assert!(is_workspace_dep(&entry));
229+
let features = extract_features(&entry).unwrap();
230+
assert_eq!(features, vec!["extra"]);
231+
}
232+
233+
#[test]
234+
fn test_build_workspace_dep_entry_optional() {
235+
let entry = build_workspace_dep_entry(None, true);
236+
assert!(is_workspace_dep(&entry));
237+
assert!(is_optional(&entry));
238+
}
239+
240+
#[test]
241+
fn test_build_transitive_entry() {
242+
let features = vec!["feature1".to_string(), "feature2".to_string()];
243+
let entry = build_transitive_entry(&features);
244+
assert!(is_workspace_dep(&entry));
245+
let extracted = extract_features(&entry).unwrap();
246+
assert_eq!(extracted, features);
247+
}
248+
}

0 commit comments

Comments
 (0)