cli_xtask/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![warn(missing_debug_implementations)]
3#![warn(missing_docs)]
4
5//! A collection of utility functions and command line interfaces for
6//! [cargo-xtask].
7//!
8//! This crate provides the following utilities:
9//!
10//! * **[`cargo xtask dist`]** and related subcommands
11//! * Builds a distributable tar.gz package for your bin crate.
12//! * **[`cargo xtask lint`]** and related subcommands
13//! * Runs the lints for your bin/lib crate.
14//! * Integrated with [`rustdoc`], [`rustfmt`], [`clippy`],
15//! [`cargo-sync-rdme`], [`cargo-udeps`].
16//! * **[`cargo xtask tidy`]** and related subcommands
17//! * Fixes the problems on your bin/lib crate.
18//! * Integrated with [`rustfmt`], [`clippy`], [`cargo-sync-rdme`].
19//! * **[`cargo xtask pre-release`]**
20//! * Checks if your bin/lib crate is ready for a release.
21//! * **[`cargo xtask build`], [`clippy`][`cargo xtask clippy`], [`doc`][`cargo
22//! xtask doc`], [`fmt`][`cargo xtask fmt`], [`test`][`cargo xtask test`]**
23//! * Runs the cargo commands with options useful for testing and continuous
24//! integration.
25//! * **`--all-workspaces`** - Runs the cargo commands for all workspaces.
26//! * **`--workspace`** - Runs the cargo commands for all packages in the
27//! workspace.
28//! * **`--each-features`** - Repeats to runs the cargo commands for each
29//! feature enabled.
30//! * **`--exhaustive`** - Same as `--all-workspaces --workspace
31//! --each-features`.
32//! * **[`cargo xtask docsrs`]**
33//! * Builds the documentation for your lib crate with configuration for
34//! [docs.rs].
35//! * **[`cargo xtask exec`]**
36//! * Runs a command in the gicontext of all workspaces.
37//!
38//! # Usage
39//!
40//! First, create an `xtask` crate following the [instructions on the
41//! cargo-xtask website][xtask-setup].
42//!
43//! Then, run the following command to add `cli-xtask` to the dependencies.
44//!
45//! * For bin crates:
46//!
47//! ```console
48//! cargo add -p xtask cli-xtask --features main,bin-crate
49//! ```
50//!
51//! If you want to use extra tools such as `cargo-sync-rdme` and
52//! `cargo-udeps`, add the `bin-crate-extra` feature.
53//!
54//! ```console
55//! cargo add -p xtask cli-xtask --features main,bin-crate,bin-crate-extra
56//! ```
57//!
58//! * For lib crates:
59//!
60//! ```console
61//! cargo add -p xtask cli-xtask --features main,lib-crate
62//! ```
63//!
64//! If you want to use extra tools such as `cargo-sync-rdme` and
65//! `cargo-udeps`, add the `lib-crate-extra` feature.
66//!
67//! ```console
68//! cargo add -p xtask cli-xtask --features main,lib-crate,lib-crate-extra
69//! ```
70//!
71//! Finally, edit `xtask/src/main.rs` as follows
72//!
73//! ```rust
74//! # #[cfg(all(feature = "main"))]
75//! # {
76//! use cli_xtask::{Result, Xtask};
77//!
78//! fn main() -> Result<()> {
79//! <Xtask>::main()
80//! }
81//! # }
82//! ```
83//!
84//! Now you can run various workflows with `cargo xtask`.
85//!
86//! [xtask-setup]: https://github.com/matklad/cargo-xtask#defining-xtasks
87//!
88//! # Customizing
89//!
90//! If you want to remove the subcommands that are not useful for your project,
91//! you can remove them by disabling the corresponding cargo features.
92//! See the [Feature flags section](#feature-flags) for more information.
93//!
94//! If you want to add the subcommands that are not included in this crate,
95//! you can add them by creating a new data structure that implements the
96//! [`clap::Subcommand`] and [`Run`].
97//! See [the documentation of `Xtask`](Xtask) for more
98//! information.
99//!
100//! # Feature flags
101//!
102//! By using the features flags of cli-xtask, you can enable only the features
103//! and commands you need. By default, all features are disabled.
104//!
105//! The following section contains a list of available features:
106//!
107//! ## CLI features
108//!
109//! * **`main`** - Enables [`Xtask::main`] function and
110//! [`Xtask::main_with_config`] function that are the premade entry point for
111//! the CLI.
112//! * **`error-handler`** - Enables functions for error handling in
113//! [`error_handler`] module.
114//! * **`logger`** - Enables functions for logging in [`logger`] module.
115//!
116//! ## Subcommand features
117//!
118//! There are two types of features that enable subcommands:
119//!
120//! * **Combined features** - features that enable several useful subcommands at
121//! once, depending on the type of crate
122//! * **Separated features** - features that enable each subcommand separately
123//!
124//! ### Combined features
125//!
126//! * **`bin-crate`**:- Enables useful subcommands for bin crates.
127//! * **`lib-crate`** - Enables useful subcommands for lib crates.
128//! * **`bin-crate-extra`** - Enables the additional subcommands useful for bin
129//! crates.
130//! * **`lib-crate-extra`** - Enables the additional subcommands useful for lib
131//! crates.
132//!
133//! The `{bin,lib}-crate` feature requires only the standard Rust tools that can
134//! be installed with `rustup`. The `{bin,lib}-crate-extra` feature may require
135//! third-party tools.
136//!
137//! ### Separated features
138//!
139//! The following features require only the standard Rust tools:
140//!
141//! * **`subcommand-build`** - Enables [`cargo xtask build`].
142//! * **`subcommand-clippy`** - Enables [`cargo xtask clippy`].
143//! * **`subcommand-dist`** - Enables [`cargo xtask dist`].
144//! * **`subcommand-dist-archive`** - Enables [`cargo xtask dist-archive`].
145//! * **`subcommand-dist-build-bin`** - Enables [`cargo xtask dist-build-bin`].
146//! * **`subcommand-dist-build-completion`** - Enables [`cargo xtask
147//! dist-build-completion`].
148//! * **`subcommand-dist-build-doc`** - Enables [`cargo xtask dist-build-doc`].
149//! * **`subcommand-dist-build-license`** - Enables [`cargo xtask
150//! dist-build-license`].
151//! * **`subcommand-dist-build-man`** - Enables [`cargo xtask dist-build-man`].
152//! * **`subcommand-dist-build-readme`** - Enables [`cargo xtask
153//! dist-build-readme`].
154//! * **`subcommand-dist-clean`** - Enables [`cargo xtask dist-clean`].
155//! * **`subcommand-doc`** - Enables [`cargo xtask doc`].
156//! * **`subcommand-docsrs`** - Enables [`cargo xtask docsrs`].
157//! * **`subcommand-exec`** - Enables [`cargo xtask exec`].
158//! * **`subcommand-fmt`** - Enables [`cargo xtask fmt`].
159//! * **`subcommand-lint`** - Enables [`cargo xtask lint`].
160//! * **`subcommand-pre-release`** - Enables [`cargo xtask pre-release`].
161//! * **`subcommand-test`** - Enables [`cargo xtask test`].
162//! * **`subcommand-tidy`** - Enables [`cargo xtask tidy`].
163//!
164//! The following features require third-party tools:
165//!
166//! * **`subcommand-sync-rdme`** - Enables [`cargo xtask sync-rdme`]. Requires
167//! [`cargo-sync-rdme`] installed.
168//! * **`subcommand-udeps`** - Enables [`cargo xtask udeps`]. Requires
169//! [`cargo-udeps`] installed.
170//!
171//! ## Other features
172//!
173//! * **`archive`** - Enables [`archive`] module which provides the
174//! functionality to create the archive file for distribution.
175//!
176//! # Minimum supported Rust version (MSRV)
177//!
178//! The minimum supported Rust version is **Rust 1.78.0**.
179//! At least the last 3 versions of stable Rust are supported at any given time.
180//!
181//! While a crate is a pre-release status (0.x.x) it may have its MSRV bumped in
182//! a patch release. Once a crate has reached 1.x, any MSRV bump will be
183//! accompanied by a new minor version.
184//!
185//! # License
186//!
187//! This project is licensed under either of
188//!
189//! * Apache License, Version 2.0 ([LICENSE-APACHE] or <http://www.apache.org/licenses/LICENSE-2.0>)
190//! * MIT license ([LICENSE-MIT] or <http://opensource.org/licenses/MIT>)
191//!
192//! at your option.
193//!
194//! # Contribution
195//!
196//! Unless you explicitly state otherwise, any contribution intentionally
197//! submitted for inclusion in the work by you, as defined in the Apache-2.0
198//! license, shall be dual licensed as above, without any additional terms or
199//! conditions.
200//!
201//! See [CONTRIBUTING.md].
202//!
203//! [cargo-xtask]: https://github.com/matklad/cargo-xtask
204//! [`cargo xtask build`]: subcommand::Build
205//! [`cargo xtask clippy`]: subcommand::Clippy
206//! [`cargo xtask dist`]: subcommand::Dist
207//! [`cargo xtask dist-archive`]: subcommand::DistArchive
208//! [`cargo xtask dist-build-bin`]: subcommand::DistBuildBin
209//! [`cargo xtask dist-build-completion`]: subcommand::DistBuildCompletion
210//! [`cargo xtask dist-build-doc`]: subcommand::DistBuildDoc
211//! [`cargo xtask dist-build-license`]: subcommand::DistBuildLicense
212//! [`cargo xtask dist-build-man`]: subcommand::DistBuildMan
213//! [`cargo xtask dist-build-readme`]: subcommand::DistBuildReadme
214//! [`cargo xtask dist-clean`]: subcommand::DistClean
215//! [`cargo xtask doc`]: subcommand::Doc
216//! [`cargo xtask docsrs`]: subcommand::Docsrs
217//! [`cargo xtask exec`]: subcommand::Exec
218//! [`cargo xtask fmt`]: subcommand::Fmt
219//! [`cargo xtask lint`]: subcommand::Lint
220//! [`cargo xtask pre-release`]: subcommand::PreRelease
221//! [`cargo xtask sync-rdme`]: subcommand::SyncRdme
222//! [`cargo xtask test`]: subcommand::Test
223//! [`cargo xtask tidy`]: subcommand::Tidy
224//! [`cargo xtask udeps`]: subcommand::Udeps
225//! [`rustdoc`]: https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html
226//! [`rustfmt`]: https://github.com/rust-lang/rustfmt
227//! [`clippy`]: https://github.com/rust-lang/rust-clippy
228//! [`cargo-sync-rdme`]: https://github.com/gifnksm/cargo-sync-rdme
229//! [`cargo-udeps`]: https://github.com/est31/cargo-udeps
230//! [docs.rs]: https://docs.rs/
231//! [LICENSE-APACHE]: https://github.com/gifnksm/cli-xtask/blob/main/LICENSE-APACHE
232//! [LICENSE-MIT]: https://github.com/gifnksm/cli-xtask/blob/main/LICENSE-MIT
233//! [CONTRIBUTING.md]: https://github.com/gifnksm/cli-xtask/blob/main/CONTRIBUTING.md
234
235#![doc(html_root_url = "https://docs.rs/cli-xtask/0.10.1")]
236
237use std::{any::Any, fmt};
238
239pub use cargo_metadata::{self, camino};
240pub use clap;
241#[cfg(feature = "error-handler")]
242#[cfg_attr(docsrs, doc(cfg(feature = "error-handler")))]
243pub use color_eyre;
244pub use eyre;
245pub use tracing;
246
247#[cfg(feature = "error-handler")]
248#[cfg_attr(docsrs, doc(cfg(feature = "error-handler")))]
249pub mod error_handler;
250
251#[cfg(feature = "logger")]
252#[cfg_attr(docsrs, doc(cfg(feature = "logger")))]
253pub use tracing_subscriber;
254
255#[cfg(feature = "logger")]
256#[cfg_attr(docsrs, doc(cfg(feature = "logger")))]
257pub mod logger;
258
259#[cfg(feature = "archive")]
260#[cfg_attr(docsrs, doc(cfg(feature = "archive")))]
261pub mod archive;
262
263pub mod args;
264pub mod cargo;
265mod command;
266pub mod config;
267pub mod fs;
268pub mod process;
269pub mod subcommand;
270pub mod workspace;
271
272pub use self::command::Xtask;
273
274/// Error type for this crate.
275pub type Error = eyre::Error;
276/// Result type for this crate.
277pub type Result<T> = eyre::Result<T>;
278
279/// Runs the command or subcommand.
280pub trait Run: Any {
281 /// Runs the command or subcommand.
282 fn run(&self, config: &config::Config) -> Result<()>;
283
284 /// Returns the subcommands that this command will run.
285 fn to_subcommands(&self) -> Option<SubcommandRun> {
286 None
287 }
288
289 /// Converts the `Box<dyn Run>` to `Box<dyn Any>`.
290 fn into_any(self: Box<Self>) -> Box<dyn Any>
291 where
292 Self: Sized,
293 {
294 self
295 }
296
297 /// Converts the `&dyn Run` trait object to a concrete type.
298 fn as_any(&self) -> &dyn Any
299 where
300 Self: Sized,
301 {
302 self
303 }
304
305 /// Converts the `&dyn Run` trait object to a mutable concrete type.
306 fn as_any_mut(&mut self) -> &mut dyn Any
307 where
308 Self: Sized,
309 {
310 self
311 }
312}
313
314/// Subcommands that this command will run.
315pub struct SubcommandRun {
316 subcommands: Vec<Box<dyn Run>>,
317}
318
319impl fmt::Debug for SubcommandRun {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 f.debug_struct("Subcommand")
322 //.field("subcommands", &self.subcommands)
323 .finish()
324 }
325}
326
327impl SubcommandRun {
328 /// Creates a new `SubcommandRun`.
329 pub fn new(subcommands: Vec<Box<dyn Run>>) -> Self {
330 Self { subcommands }
331 }
332
333 /// Returns the subcommands that this command will run.
334 pub fn subcommands(&self) -> &[Box<dyn Run>] {
335 &self.subcommands
336 }
337
338 /// Returns the subcommands that this command will run.
339 pub fn subcommands_mut(&mut self) -> &mut Vec<Box<dyn Run>> {
340 &mut self.subcommands
341 }
342
343 /// Runs the subcommands.
344 pub fn run(&self, config: &config::Config) -> Result<()> {
345 for subcommand in &self.subcommands {
346 subcommand.run(config)?;
347 }
348 Ok(())
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 struct S(i32);
357 impl Run for S {
358 fn run(&self, _config: &config::Config) -> Result<()> {
359 Ok(())
360 }
361 }
362
363 #[test]
364 fn run_downcast() {
365 let s = &S(42);
366 let s = s.as_any().downcast_ref::<S>().unwrap();
367 assert_eq!(s.0, 42);
368
369 let s = &mut S(42);
370 let s = s.as_any_mut().downcast_mut::<S>().unwrap();
371 assert_eq!(s.0, 42);
372
373 let s = Box::new(S(42));
374 let s = s.into_any().downcast::<S>().unwrap();
375 assert_eq!(s.0, 42);
376 }
377}