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}