Skip to main content

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