Skip to content

Commit c63671d

Browse files
committed
[WIP] add a command to install man pages
1 parent 3221f1d commit c63671d

File tree

7 files changed

+135
-3
lines changed

7 files changed

+135
-3
lines changed

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cargo-nextest/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rust-version = "1.59"
1515
camino = "1.0.9"
1616
cfg-if = "1.0.0"
1717
clap = { version = "3.2.6", features = ["derive", "env"] }
18+
clap_mangen = "0.1.9"
1819
# we don't use the tracing support
1920
color-eyre = { version = "0.6.1", default-features = false }
2021
dialoguer = "0.10.1"
@@ -23,6 +24,7 @@ enable-ansi-support = "0.1.2"
2324
# we don't use the default formatter so we don't need default features
2425
env_logger = { version = "0.9.0", default-features = false }
2526
guppy = "0.14.2"
27+
home = "0.5.3"
2628
log = "0.4.17"
2729
itertools = "0.10.3"
2830
miette = { version = "4.7.1", features = ["fancy"] }

cargo-nextest/src/dispatch.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::{
55
cargo_cli::{CargoCli, CargoOptions},
6+
mangen::install_man,
67
output::{OutputContext, OutputOpts, OutputWriter},
78
reuse_build::{make_path_mapper, ArchiveFormatOpt, ReuseBuildOpts},
89
ExpectedError, Result, ReuseBuildKind,
@@ -55,14 +56,20 @@ impl CargoNextestApp {
5556
}
5657

5758
#[derive(Debug, Subcommand)]
58-
enum NextestSubcommand {
59+
pub(crate) enum NextestSubcommand {
5960
/// A next-generation test runner for Rust. <https://nexte.st>
6061
Nextest(AppOpts),
6162
}
6263

63-
#[derive(Debug, Args)]
64+
/// cargo-nextest is a next-generation test runner for Rust projects.
65+
///
66+
/// Nextest runs tests in parallel and provides a rich set of features, such as partitioning test
67+
/// runs, JUnit output, and archiving and reusing builds.
68+
///
69+
/// For the full documentation, see the nextest site at <https://nexte.st>.
70+
#[derive(Debug, Parser)]
6471
#[clap(version)]
65-
struct AppOpts {
72+
pub(crate) struct AppOpts {
6673
/// Path to Cargo.toml
6774
#[clap(long, global = true, value_name = "PATH")]
6875
manifest_path: Option<Utf8PathBuf>,
@@ -1018,6 +1025,12 @@ impl App {
10181025

10191026
#[derive(Debug, Subcommand)]
10201027
enum SelfCommand {
1028+
/// Install man pages for nextest.
1029+
InstallMan {
1030+
/// The output directory [default: <current-exe-path>/../man]
1031+
output_dir: Option<Utf8PathBuf>,
1032+
},
1033+
10211034
#[cfg_attr(
10221035
not(feature = "self-update"),
10231036
doc = "This version of nextest does not have self-update enabled\n\
@@ -1064,6 +1077,10 @@ impl SelfCommand {
10641077
let output = output.init();
10651078

10661079
match self {
1080+
Self::InstallMan { output_dir } => {
1081+
install_man(output_dir)?;
1082+
Ok(0)
1083+
}
10671084
Self::Update {
10681085
version,
10691086
check,

cargo-nextest/src/errors.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ pub enum ExpectedError {
172172
reason: &'static str,
173173
args: Vec<String>,
174174
},
175+
#[error(transparent)]
176+
InstallManError {
177+
#[from]
178+
error: InstallManError,
179+
},
175180
}
176181

177182
impl ExpectedError {
@@ -311,6 +316,7 @@ impl ExpectedError {
311316
NextestExitCode::EXPERIMENTAL_FEATURE_NOT_ENABLED
312317
}
313318
Self::FilterExpressionParseError { .. } => NextestExitCode::INVALID_FILTER_EXPRESSION,
319+
Self::InstallManError { .. } => NextestExitCode::INSTALL_MAN_ERROR,
314320
}
315321
}
316322

@@ -529,6 +535,11 @@ impl ExpectedError {
529535
);
530536
None
531537
}
538+
Self::InstallManError { error } => {
539+
// This is a transparent error.
540+
log::error!("{}", error);
541+
error.source()
542+
}
532543
};
533544

534545
while let Some(err) = next_error {
@@ -537,3 +548,25 @@ impl ExpectedError {
537548
}
538549
}
539550
}
551+
552+
#[derive(Debug, Error)]
553+
#[doc(hidden)]
554+
pub enum InstallManError {
555+
#[error("could not determine current executable path")]
556+
CurrentExe {
557+
#[source]
558+
error: std::io::Error,
559+
},
560+
#[error("error creating output directory `{path}`")]
561+
CreateOutputDir {
562+
path: Utf8PathBuf,
563+
#[source]
564+
error: std::io::Error,
565+
},
566+
#[error("error writing to `{path}`")]
567+
WriteToFile {
568+
path: Utf8PathBuf,
569+
#[source]
570+
error: std::io::Error,
571+
},
572+
}

cargo-nextest/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
mod cargo_cli;
1717
mod dispatch;
1818
mod errors;
19+
mod mangen;
1920
mod output;
2021
mod reuse_build;
2122
#[cfg(feature = "self-update")]

cargo-nextest/src/mangen.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) The nextest Contributors
2+
// SPDX-License-Identifier: MIT OR Apache-2.0
3+
4+
use crate::{AppOpts, InstallManError};
5+
use camino::{Utf8Path, Utf8PathBuf};
6+
use clap::CommandFactory;
7+
use clap_mangen::Man;
8+
9+
pub(crate) fn install_man(output_dir: Option<Utf8PathBuf>) -> Result<(), InstallManError> {
10+
let mut output_dir = match output_dir {
11+
Some(d) => d,
12+
None => {
13+
let mut current_exe = std::env::current_exe()
14+
.and_then(|home| {
15+
Utf8PathBuf::try_from(home).map_err(|error| {
16+
std::io::Error::new(std::io::ErrorKind::InvalidData, error)
17+
})
18+
})
19+
.map_err(|error| InstallManError::CurrentExe { error })?;
20+
// If the current exe is foo/bar/bin/cargo-nextest, the man directory is foo/bar/man.
21+
current_exe.pop();
22+
current_exe.pop();
23+
current_exe.push("man");
24+
current_exe
25+
}
26+
};
27+
28+
// All of nextest's commands go in man1.
29+
output_dir.push("man1");
30+
31+
std::fs::create_dir_all(&output_dir).map_err(|error| InstallManError::CreateOutputDir {
32+
path: output_dir.clone(),
33+
error,
34+
})?;
35+
36+
let command = AppOpts::command();
37+
38+
let man = Man::new(command.clone()).manual("Nextest Manual");
39+
let path = output_dir.join("cargo-nextest.1");
40+
render_to_file(&man, &path).map_err(|error| InstallManError::WriteToFile { path, error })?;
41+
42+
for subcommand in command.get_subcommands() {
43+
let name = subcommand.get_name();
44+
// XXX this line crashes with "Command list: Argument or group 'manifest-path' specified in
45+
// 'conflicts_with*' for 'cargo-metadata' does not exist".
46+
let man = Man::new(subcommand.clone()).manual("Nextest Manual");
47+
let path = output_dir.join(format!("cargo-nextest-{}.1", name));
48+
render_to_file(&man, &path)
49+
.map_err(|error| InstallManError::WriteToFile { path, error })?;
50+
}
51+
52+
Ok(())
53+
}
54+
55+
fn render_to_file(man: &Man, path: &Utf8Path) -> Result<(), std::io::Error> {
56+
let mut writer = std::fs::File::create(&path)?;
57+
man.render(&mut writer)
58+
}

nextest-metadata/src/exit_codes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ impl NextestExitCode {
2828
/// Writing data to stdout or stderr produced an error.
2929
pub const WRITE_OUTPUT_ERROR: i32 = 110;
3030

31+
/// Installing man pages produced an error.
32+
pub const INSTALL_MAN_ERROR: i32 = 120;
33+
3134
/// Downloading an update resulted in an error.
3235
pub const UPDATE_ERROR: i32 = 90;
3336

0 commit comments

Comments
 (0)