diff --git a/src/bin/cargo/commands/locate_project.rs b/src/bin/cargo/commands/locate_project.rs index c1c8435e459..ee790f526d2 100644 --- a/src/bin/cargo/commands/locate_project.rs +++ b/src/bin/cargo/commands/locate_project.rs @@ -1,11 +1,20 @@ use crate::command_prelude::*; +use anyhow::{bail, Context, Result}; +use cargo::drop_println; +use cargo::util::format::{Parser, RawChunk}; use serde::Serialize; +use std::fmt; pub fn cli() -> App { subcommand("locate-project") .about("Print a JSON representation of a Cargo.toml file's location") .arg(opt("quiet", "No output printed to stdout").short("q")) .arg_manifest_path() + .arg( + opt("format", "Format string used for printing project path") + .value_name("FORMAT") + .short("f"), + ) .after_help("Run `cargo help locate-project` for more detailed information.\n") } @@ -29,6 +38,54 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { let location = ProjectLocation { root }; - config.shell().print_json(&location); + match args.value_of("format") { + None => config.shell().print_json(&location), + Some(format) => print_format(config, &location, format)?, + } + + Ok(()) +} + +enum Chunk<'a> { + Raw(&'a str), + Root, +} + +fn parse_format(format: &str) -> Result>> { + let mut chunks = Vec::new(); + for raw in Parser::new(format) { + chunks.push(match raw { + RawChunk::Text(text) => Chunk::Raw(text), + RawChunk::Argument("root") => Chunk::Root, + RawChunk::Argument(a) => bail!("unsupported pattern `{}`", a), + RawChunk::Error(err) => bail!("{}", err), + }); + } + Ok(chunks) +} + +struct Display<'a> { + format: &'a [Chunk<'a>], + location: &'a ProjectLocation<'a>, +} + +impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for chunk in self.format { + match chunk { + Chunk::Raw(s) => f.write_str(s)?, + Chunk::Root => f.write_str(self.location.root)?, + } + } + Ok(()) + } +} + +fn print_format(config: &mut Config, location: &ProjectLocation<'_>, format: &str) -> Result<()> { + let ref format = parse_format(format) + .with_context(|| format!("locate-project format `{}` not valid", format))?; + + let display = Display { format, location }; + drop_println!(config, "{}", display); Ok(()) } diff --git a/src/cargo/ops/tree/format/mod.rs b/src/cargo/ops/tree/format.rs similarity index 98% rename from src/cargo/ops/tree/format/mod.rs rename to src/cargo/ops/tree/format.rs index c1d8892c831..cfbe26eab88 100644 --- a/src/cargo/ops/tree/format/mod.rs +++ b/src/cargo/ops/tree/format.rs @@ -1,10 +1,8 @@ -use self::parse::{Parser, RawChunk}; use super::{Graph, Node}; +use crate::util::format::{Parser, RawChunk}; use anyhow::{bail, Error}; use std::fmt; -mod parse; - enum Chunk { Raw(String), Package, diff --git a/src/cargo/ops/tree/format/parse.rs b/src/cargo/util/format.rs similarity index 100% rename from src/cargo/ops/tree/format/parse.rs rename to src/cargo/util/format.rs diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs index 8bed0b91633..352b5f0bbbd 100644 --- a/src/cargo/util/mod.rs +++ b/src/cargo/util/mod.rs @@ -39,6 +39,7 @@ mod dependency_queue; pub mod diagnostic_server; pub mod errors; mod flock; +pub mod format; pub mod graph; mod hasher; pub mod hex; diff --git a/src/doc/man/cargo-locate-project.md b/src/doc/man/cargo-locate-project.md index 349e63c5e8d..55d72c9dc26 100644 --- a/src/doc/man/cargo-locate-project.md +++ b/src/doc/man/cargo-locate-project.md @@ -18,6 +18,26 @@ workspace root. ## OPTIONS +### Formatting Options + +{{#options}} + +{{#option "`-f` _format_" "`--format` _format_" }} +Set the format string for the output representation. The default is a JSON +object holding all of the supported information. + +This is an arbitrary string which will be used to display the project location. +The following strings will be replaced with the corresponding value: + +- `{root}` — The absolute path of the `Cargo.toml` manifest. + +For example you might use `--format '{root}'` or more concisely `-f{root}` to +output just the path and nothing else, or `--format 'root={root}'` to include a +prefix. +{{/option}} + +{{/options}} + ### Display Options {{#options}} diff --git a/src/doc/man/generated_txt/cargo-locate-project.txt b/src/doc/man/generated_txt/cargo-locate-project.txt index ec1377fb568..18c69aa0d33 100644 --- a/src/doc/man/generated_txt/cargo-locate-project.txt +++ b/src/doc/man/generated_txt/cargo-locate-project.txt @@ -15,6 +15,21 @@ DESCRIPTION workspace root. OPTIONS + Formatting Options + -f format, --format format + Set the format string for the output representation. The default is + a JSON object holding all of the supported information. + + This is an arbitrary string which will be used to display the + project location. The following strings will be replaced with the + corresponding value: + + o {root} — The absolute path of the Cargo.toml manifest. + + For example you might use --format '{root}' or more concisely + -f{root} to output just the path and nothing else, or --format + 'root={root}' to include a prefix. + Display Options -v, --verbose Use verbose output. May be specified twice for "very verbose" output diff --git a/src/doc/src/commands/cargo-locate-project.md b/src/doc/src/commands/cargo-locate-project.md index dff40101277..efc16b92423 100644 --- a/src/doc/src/commands/cargo-locate-project.md +++ b/src/doc/src/commands/cargo-locate-project.md @@ -18,6 +18,26 @@ workspace root. ## OPTIONS +### Formatting Options + +
+ +
-f format
+
--format format
+
Set the format string for the output representation. The default is a JSON +object holding all of the supported information.

+

This is an arbitrary string which will be used to display the project location. +The following strings will be replaced with the corresponding value:

+
    +
  • {root} — The absolute path of the Cargo.toml manifest.
  • +
+

For example you might use --format '{root}' or more concisely -f{root} to +output just the path and nothing else, or --format 'root={root}' to include a +prefix.

+ + +
+ ### Display Options
diff --git a/src/etc/_cargo b/src/etc/_cargo index f906d149168..258801c9d41 100644 --- a/src/etc/_cargo +++ b/src/etc/_cargo @@ -160,7 +160,8 @@ _cargo() { ;; locate-project) - _arguments -s -S $common $manifest + _arguments -s -S $common $manifest \ + '(-f --format)'{-f,--format=}'[format string]:format' ;; login) diff --git a/src/etc/cargo.bashcomp.sh b/src/etc/cargo.bashcomp.sh index aa6626f1114..6e024994d16 100644 --- a/src/etc/cargo.bashcomp.sh +++ b/src/etc/cargo.bashcomp.sh @@ -59,7 +59,7 @@ _cargo() local opt__help="$opt_help" local opt__init="$opt_common $opt_lock --bin --lib --name --vcs --edition --registry" local opt__install="$opt_common $opt_feat $opt_jobs $opt_lock $opt_force --bin --bins --branch --debug --example --examples --git --list --path --rev --root --tag --version --registry --target --profile --no-track" - local opt__locate_project="$opt_common $opt_mani $opt_lock" + local opt__locate_project="$opt_common $opt_mani $opt_lock -f --format" local opt__login="$opt_common $opt_lock --registry" local opt__metadata="$opt_common $opt_feat $opt_mani $opt_lock --format-version=1 --no-deps --filter-platform" local opt__new="$opt_common $opt_lock --vcs --bin --lib --name --edition --registry" diff --git a/src/etc/man/cargo-locate-project.1 b/src/etc/man/cargo-locate-project.1 index 29283dde5ac..2a481e79d7b 100644 --- a/src/etc/man/cargo-locate-project.1 +++ b/src/etc/man/cargo-locate-project.1 @@ -14,6 +14,25 @@ This command will print a JSON object to stdout with the full path to the See also \fBcargo\-metadata\fR(1) which is capable of returning the path to a workspace root. .SH "OPTIONS" +.SS "Formatting Options" +.sp +\fB\-f\fR \fIformat\fR, +\fB\-\-format\fR \fIformat\fR +.RS 4 +Set the format string for the output representation. The default is a JSON +object holding all of the supported information. +.sp +This is an arbitrary string which will be used to display the project location. +The following strings will be replaced with the corresponding value: +.sp +.RS 4 +\h'-04'\(bu\h'+02'\fB{root}\fR \[em] The absolute path of the \fBCargo.toml\fR manifest. +.RE +.sp +For example you might use \fB\-\-format '{root}'\fR or more concisely \fB\-f{root}\fR to +output just the path and nothing else, or \fB\-\-format 'root={root}'\fR to include a +prefix. +.RE .SS "Display Options" .sp \fB\-v\fR, diff --git a/tests/testsuite/locate_project.rs b/tests/testsuite/locate_project.rs index 17712a69c24..0a358cf0646 100644 --- a/tests/testsuite/locate_project.rs +++ b/tests/testsuite/locate_project.rs @@ -14,3 +14,34 @@ fn simple() { )) .run(); } + +#[cargo_test] +fn format() { + let p = project().build(); + let root_manifest_path = p.root().join("Cargo.toml"); + let root_str = root_manifest_path.to_str().unwrap(); + + p.cargo("locate-project --format {root}") + .with_stdout(root_str) + .run(); + + p.cargo("locate-project -f{root}") + .with_stdout(root_str) + .run(); + + p.cargo("locate-project --format root={root}") + .with_stdout(format!("root={}", root_str)) + .run(); + + p.cargo("locate-project --format {toor}") + .with_stderr( + "\ +[ERROR] locate-project format `{toor}` not valid + +Caused by: + unsupported pattern `toor` +", + ) + .with_status(101) + .run(); +}