diff --git a/cargo-workspaces/src/init.rs b/cargo-workspaces/src/init.rs new file mode 100644 index 0000000..30d4154 --- /dev/null +++ b/cargo-workspaces/src/init.rs @@ -0,0 +1,71 @@ +use crate::utils::info; +use crate::utils::{Error, Result}; +use cargo_metadata::MetadataCommand; +use clap::Clap; +use glob::glob; +use std::{collections::HashSet, fs, path::PathBuf}; + +/// Create a new cargo workspace in an existing directory +#[derive(Debug, Clap)] +pub struct Init { + /// Path to the workspace root + #[clap(parse(from_os_str), default_value = ".")] + path: PathBuf, +} + +impl Init { + pub fn run(&self) -> Result { + if !self.path.is_dir() { + return Err(Error::Init(format!( + "the path `{}` does not exist", + self.path.display() + ))); + } + + let cargo_toml = self.path.join("Cargo.toml"); + + if cargo_toml.is_file() { + return Err(Error::Init(format!( + "`init` cannot be run on existing Cargo packages." + ))); + } + + let ws = fs::canonicalize(&self.path)?; + + let mut workspace_roots = HashSet::new(); + + for path in glob(&format!("{}/**/Cargo.toml", self.path.display()))?.filter_map(|e| e.ok()) + { + let metadata = MetadataCommand::default() + .manifest_path(path) + .exec() + .map_err(|e| Error::Init(e.to_string()))?; + workspace_roots.insert(metadata.workspace_root); + } + + let mut content = "[workspace]\nmembers = [".to_string(); + + let mut members: Vec<_> = workspace_roots + .iter() + .filter_map(|m| m.strip_prefix(&ws).ok()) + .collect(); + + members.sort(); + if !members.is_empty() { + content.push_str("\n"); + } + + members + .into_iter() + .for_each(|m| content.push_str(&format!(" \"{}\",\n", m.display()))); + content.push_str("]"); + + fs::write(cargo_toml, content)?; + + info!( + "success", + format!("Initialized workspace `{}`.", self.path.display()) + )?; + Ok(()) + } +} diff --git a/cargo-workspaces/src/main.rs b/cargo-workspaces/src/main.rs index 1d99505..d2940d9 100644 --- a/cargo-workspaces/src/main.rs +++ b/cargo-workspaces/src/main.rs @@ -6,6 +6,7 @@ use std::process::exit; mod changed; mod create; mod exec; +mod init; mod list; mod publish; mod rename; @@ -23,6 +24,7 @@ enum Subcommand { Exec(exec::Exec), Create(create::Create), Rename(rename::Rename), + Init(init::Init), } #[derive(Debug, Clap)] @@ -64,25 +66,30 @@ fn main() { utils::set_debug(); } - let mut cmd = MetadataCommand::new(); - - cmd.features(CargoOpt::AllFeatures); - cmd.no_deps(); - - if let Some(path) = opt.manifest_path { - cmd.manifest_path(path); - } - - let metadata = cmd.exec().unwrap(); - - let err = match opt.subcommand { - Subcommand::List(x) => x.run(metadata), - Subcommand::Changed(x) => x.run(metadata), - Subcommand::Version(x) => x.run(metadata), - Subcommand::Publish(x) => x.run(metadata), - Subcommand::Exec(x) => x.run(metadata), - Subcommand::Create(x) => x.run(metadata), - Subcommand::Rename(x) => x.run(metadata), + let err = if let Subcommand::Init(ref init) = opt.subcommand { + init.run() + } else { + let mut cmd = MetadataCommand::new(); + + cmd.features(CargoOpt::AllFeatures); + cmd.no_deps(); + + if let Some(path) = opt.manifest_path { + cmd.manifest_path(path); + } + + let metadata = cmd.exec().unwrap(); + + match opt.subcommand { + Subcommand::List(x) => x.run(metadata), + Subcommand::Changed(x) => x.run(metadata), + Subcommand::Version(x) => x.run(metadata), + Subcommand::Publish(x) => x.run(metadata), + Subcommand::Exec(x) => x.run(metadata), + Subcommand::Create(x) => x.run(metadata), + Subcommand::Rename(x) => x.run(metadata), + _ => unreachable!(), + } } .err(); diff --git a/cargo-workspaces/src/utils/error.rs b/cargo-workspaces/src/utils/error.rs index 3a5f6f0..f2fba1c 100644 --- a/cargo-workspaces/src/utils/error.rs +++ b/cargo-workspaces/src/utils/error.rs @@ -68,6 +68,8 @@ pub enum Error { Update, #[error("unable to create crate")] Create, + #[error("unable to initialize workspace: {0}")] + Init(String), #[error("package {0}'s manifest has not parent directory")] ManifestHasNoParent(String), #[error("unable to update crate index, got {0}")]