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();
+}