forked from killercup/cargo-edit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refs killercup#338
- Loading branch information
Showing
3 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
//! `cargo set-version` | ||
#![warn( | ||
missing_docs, | ||
missing_debug_implementations, | ||
missing_copy_implementations, | ||
trivial_casts, | ||
trivial_numeric_casts, | ||
unsafe_code, | ||
unstable_features, | ||
unused_import_braces, | ||
unused_qualifications | ||
)] | ||
|
||
#[macro_use] | ||
extern crate error_chain; | ||
|
||
use crate::errors::*; | ||
use cargo_edit::{find, LocalManifest}; | ||
use failure::Fail; | ||
use std::io::Write; | ||
use std::path::{Path, PathBuf}; | ||
use std::process; | ||
use structopt::StructOpt; | ||
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; | ||
use semver::Version; | ||
|
||
mod errors { | ||
error_chain! { | ||
links { | ||
CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind); | ||
} | ||
foreign_links { | ||
CargoMetadata(::failure::Compat<::cargo_metadata::Error>); | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, StructOpt)] | ||
#[structopt(bin_name = "cargo")] | ||
enum Command { | ||
/// set-version dependencies as specified in the local manifest file (i.e. Cargo.toml). | ||
#[structopt(name = "set-version")] | ||
#[structopt( | ||
after_help = "This command differs from `cargo update`, which updates the dependency versions recorded in the | ||
local lock file (Cargo.lock). | ||
If `<dependency>`(s) are provided, only the specified dependencies will be upgraded. The version to | ||
upgrade to for each can be specified with e.g. `[email protected]` or `serde@>=0.9,<2.0`. | ||
Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io are | ||
supported. Git/path dependencies will be ignored. | ||
All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may | ||
be supplied in the presence of a virtual manifest. | ||
If the '--to-lockfile' flag is supplied, all dependencies will be upgraded to the currently locked | ||
version as recorded in the Cargo.lock file. This flag requires that the Cargo.lock file is | ||
up-to-date. If the lock file is missing, or it needs to be updated, cargo-upgrade will exit with an | ||
error. If the '--to-lockfile' flag is supplied then the network won't be accessed." | ||
)] | ||
SetVersion(Args), | ||
} | ||
|
||
#[derive(Debug, StructOpt)] | ||
struct Args { | ||
/// Path to the manifest to set version | ||
#[structopt(long = "manifest-path", value_name = "path")] | ||
manifest_path: Option<PathBuf>, | ||
|
||
/// Set version of all packages in the workspace. | ||
#[structopt(long = "all")] | ||
all: bool, | ||
|
||
/// Set a specific semantic version. | ||
#[structopt(long = "version")] | ||
version: Version, | ||
|
||
/// Print changes to be made without making them. | ||
#[structopt(long = "dry-run")] | ||
dry_run: bool, | ||
} | ||
|
||
/// A collection of manifests. | ||
struct Manifests(Vec<(LocalManifest, cargo_metadata::Package)>); | ||
|
||
fn dry_run_message() -> Result<()> { | ||
let bufwtr = BufferWriter::stdout(ColorChoice::Always); | ||
let mut buffer = bufwtr.buffer(); | ||
buffer | ||
.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true)) | ||
.chain_err(|| "Failed to set output colour")?; | ||
write!(&mut buffer, "Starting dry run. ").chain_err(|| "Failed to write dry run message")?; | ||
buffer | ||
.set_color(&ColorSpec::new()) | ||
.chain_err(|| "Failed to clear output colour")?; | ||
writeln!(&mut buffer, "Changes will not be saved.") | ||
.chain_err(|| "Failed to write dry run message")?; | ||
bufwtr | ||
.print(&buffer) | ||
.chain_err(|| "Failed to print dry run message") | ||
} | ||
|
||
impl Manifests { | ||
/// Get all manifests in the workspace. | ||
fn get_all(manifest_path: &Option<PathBuf>) -> Result<Self> { | ||
let mut cmd = cargo_metadata::MetadataCommand::new(); | ||
cmd.no_deps(); | ||
if let Some(path) = manifest_path { | ||
cmd.manifest_path(path); | ||
} | ||
let result = cmd.exec().map_err(|e| { | ||
Error::from(e.compat()).chain_err(|| "Failed to get workspace metadata") | ||
})?; | ||
result | ||
.packages | ||
.into_iter() | ||
.map(|package| { | ||
Ok(( | ||
LocalManifest::try_new(Path::new(&package.manifest_path))?, | ||
package, | ||
)) | ||
}) | ||
.collect::<Result<Vec<_>>>() | ||
.map(Manifests) | ||
} | ||
|
||
/// Get the manifest specified by the manifest path. Try to make an educated guess if no path is | ||
/// provided. | ||
fn get_local_one(manifest_path: &Option<PathBuf>) -> Result<Self> { | ||
let resolved_manifest_path: String = find(&manifest_path)?.to_string_lossy().into(); | ||
|
||
let manifest = LocalManifest::find(&manifest_path)?; | ||
|
||
let mut cmd = cargo_metadata::MetadataCommand::new(); | ||
cmd.no_deps(); | ||
if let Some(path) = manifest_path { | ||
cmd.manifest_path(path); | ||
} | ||
let result = cmd | ||
.exec() | ||
.map_err(|e| Error::from(e.compat()).chain_err(|| "Invalid manifest"))?; | ||
let packages = result.packages; | ||
let package = packages | ||
.iter() | ||
.find(|p| p.manifest_path.to_string_lossy() == resolved_manifest_path) | ||
// If we have successfully got metadata, but our manifest path does not correspond to a | ||
// package, we must have been called against a virtual manifest. | ||
.chain_err(|| { | ||
"Found virtual manifest, but this command requires running against an \ | ||
actual package in this workspace. Try adding `--all`." | ||
})?; | ||
|
||
Ok(Manifests(vec![(manifest, package.to_owned())])) | ||
} | ||
|
||
/// Upgrade the manifests on disk following the previously-determined upgrade schema. | ||
fn set_version(self, dry_run: bool, version: Version) -> Result<()> { | ||
if dry_run { | ||
dry_run_message()?; | ||
} | ||
|
||
for (mut manifest, package) in self.0 { | ||
println!("{}:", package.name); | ||
manifest.set_version(dry_run, &version)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error | ||
/// messages. | ||
fn process(args: Args) -> Result<()> { | ||
let Args { | ||
manifest_path, | ||
all, | ||
version, | ||
dry_run, | ||
} = args; | ||
|
||
let manifests = if all { | ||
Manifests::get_all(&manifest_path) | ||
} else { | ||
Manifests::get_local_one(&manifest_path) | ||
}?; | ||
|
||
manifests.set_version(dry_run, version) | ||
} | ||
|
||
fn main() { | ||
let args: Command = Command::from_args(); | ||
let Command::SetVersion(args) = args; | ||
|
||
if let Err(err) = process(args) { | ||
eprintln!("Command failed due to unhandled error: {}\n", err); | ||
|
||
for e in err.iter().skip(1) { | ||
eprintln!("Caused by: {}", e); | ||
} | ||
|
||
if let Some(backtrace) = err.backtrace() { | ||
eprintln!("Backtrace: {:?}", backtrace); | ||
} | ||
|
||
process::exit(1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters