use assert_cmd::Command;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command as StdCommand;
use tempfile::TempDir;
pub struct WorkspaceFixture {
_temp_dir: TempDir,
manifest_path: PathBuf,
}
impl WorkspaceFixture {
pub fn manifest_path(&self) -> &Path {
&self.manifest_path
}
}
pub fn cargo_focus() -> Command {
assert_cmd::cargo::cargo_bin_cmd!("cargo-focus")
}
pub fn cargo_focus_for_manifest(fixture: &WorkspaceFixture) -> Command {
let mut cmd = cargo_focus();
cmd.arg("--manifest-path")
.arg(fixture.manifest_path().to_str().expect("manifest path"));
cmd
}
pub fn create_workspace_fixture(with_presets: bool) -> WorkspaceFixture {
create_workspace_fixture_with_layout(
with_presets,
&[
("foo", "foo"),
("bar", "bar"),
("baz", "baz"),
("ambiguous", "ambiguous"),
],
)
}
#[allow(dead_code)]
pub fn create_nested_workspace_fixture(with_presets: bool) -> WorkspaceFixture {
create_workspace_fixture_with_layout(
with_presets,
&[
("foo", "foo"),
("bar", "crates/bar"),
("baz", "crates/baz"),
("ambiguous", "ambiguous"),
],
)
}
#[allow(dead_code)]
pub fn create_mismatched_member_path_fixture(with_presets: bool) -> WorkspaceFixture {
create_workspace_fixture_with_layout(
with_presets,
&[
("foo", "foo"),
("sp-core", "substrate/primitives/core"),
("bar", "bar"),
],
)
}
fn create_workspace_fixture_with_layout(
with_presets: bool,
members: &[(&str, &str)],
) -> WorkspaceFixture {
let temp_dir = TempDir::new().expect("temp dir");
let root = temp_dir.path().to_path_buf();
write_file(
&root.join("Cargo.toml"),
&workspace_manifest(with_presets, members),
);
for (name, path) in members {
write_member_crate(&root, name, path);
}
generate_lockfile(&root.join("Cargo.toml"));
WorkspaceFixture {
_temp_dir: temp_dir,
manifest_path: root.join("Cargo.toml"),
}
}
fn generate_lockfile(manifest_path: &Path) {
let status = StdCommand::new("cargo")
.args(["generate-lockfile", "--manifest-path"])
.arg(manifest_path)
.status()
.expect("run cargo generate-lockfile");
assert!(status.success(), "cargo generate-lockfile failed");
}
fn workspace_manifest(with_presets: bool, members: &[(&str, &str)]) -> String {
let members_list = members
.iter()
.map(|(_, path)| format!("\"{path}\""))
.collect::<Vec<_>>()
.join(", ");
let default_member = members.first().map(|(_, path)| *path).unwrap_or("foo");
let mut manifest = format!(
"[workspace]\nmembers = [{members_list}]\ndefault-members = [\"{default_member}\"]\nresolver = \"3\"\n"
);
if with_presets {
manifest.push_str(
r#"
[workspace.metadata.cargo-focus]
schema = "1"
[workspace.metadata.cargo-focus.presets.dev]
description = "dev preset"
members = ["bar", "baz"]
[workspace.metadata.cargo-focus.presets.ambiguous]
description = "name collides with crate"
members = ["bar"]
"#,
);
}
manifest
}
fn write_member_crate(root: &Path, name: &str, rel_path: &str) {
write_file(
&root.join(rel_path).join("Cargo.toml"),
&format!(
r#"[package]
name = "{name}"
version = "0.1.0"
edition = "2024"
"#
),
);
write_file(
&root.join(rel_path).join("src").join("lib.rs"),
"pub fn marker() {}\n",
);
}
fn write_file(path: &Path, content: &str) {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).expect("create parent dirs");
}
fs::write(path, content).expect("write file");
}