Skip to main content

cargo_sync_rdme/
lib.rs

1//! Cargo subcommand to synchronize README with the cargo manifest and crate
2//! documentation.
3//!
4//! See [repository's README] for `cargo-sync-rdme` command usage.
5//!
6//! # Usage
7//!
8//! Add this to your `Cargo.toml`:
9//!
10//! ```toml
11//! [dependencies]
12//! cargo-sync-rdme = "0.5.0"
13//! ```
14//!
15//! [repository's README]: https://github.com/gifnksm/cargo-sync-rdme/blob/main/README.md
16#![doc(html_root_url = "https://docs.rs/cargo-sync-rdme/0.5.0")]
17#![warn(
18    elided_lifetimes_in_paths,
19    explicit_outlives_requirements,
20    keyword_idents,
21    missing_copy_implementations,
22    missing_debug_implementations,
23    missing_docs,
24    single_use_lifetimes,
25    unreachable_pub,
26    unused
27)]
28
29use std::{env, io, process};
30
31use clap::{CommandFactory as _, Parser};
32use clap_complete::{Generator, Shell};
33use tracing::Level;
34use tracing_subscriber::{EnvFilter, filter::LevelFilter};
35
36#[macro_use]
37mod macros;
38
39mod cli;
40mod config;
41mod diff;
42mod sync;
43mod traits;
44mod vcs;
45mod with_source;
46
47pub use self::cli::App;
48
49/// Error type for `cargo-sync-rdme` command.
50pub type Error = miette::Error;
51
52/// Result type for `cargo-sync-rdme` command.
53pub type Result<T> = miette::Result<T>;
54
55/// Entry point of `cargo-sync-rdme` command.
56pub fn main(bin_name: &str) -> Result<()> {
57    let env_prefix = bin_name.to_uppercase().replace("-", "_");
58    if let Ok(shell) = env::var(format!("{env_prefix}_PRINT_COMPLETION")) {
59        print_completion(bin_name, &shell);
60        process::exit(0);
61    }
62    if let Ok(output_dir) = env::var(format!("{env_prefix}_GENERATE_MAN_TO")) {
63        generate_man(&output_dir);
64        process::exit(0);
65    }
66
67    // If this command is run by cargo, the first argument is the subcommand name
68    // `sync-rdme`. We need to remove it to avoid parsing error.
69    let args = env::args().enumerate().filter_map(|(idx, arg)| {
70        if idx == 1 && arg == "sync-rdme" {
71            None
72        } else {
73            Some(arg)
74        }
75    });
76    let app = App::parse_from(args);
77    install_logger(app.verbosity.into())?;
78
79    let workspace = app.workspace.metadata()?;
80    for package in app.package.packages(&workspace)? {
81        sync::sync_all(&app, &workspace, package)?;
82    }
83
84    Ok(())
85}
86
87fn install_logger(verbosity: Option<Level>) -> Result<()> {
88    let env_filter = if env::var_os("RUST_LOG").is_some() {
89        EnvFilter::from_default_env()
90    } else {
91        let default_level = match verbosity {
92            Some(Level::ERROR) => LevelFilter::ERROR,
93            Some(Level::WARN) => LevelFilter::WARN,
94            Some(Level::INFO) => LevelFilter::INFO,
95            Some(Level::DEBUG) => LevelFilter::DEBUG,
96            Some(Level::TRACE) => LevelFilter::TRACE,
97            None => LevelFilter::OFF,
98        };
99        EnvFilter::builder()
100            .with_default_directive(default_level.into())
101            .from_env_lossy()
102    };
103
104    tracing_subscriber::fmt()
105        .with_env_filter(env_filter)
106        .with_writer(io::stderr)
107        .with_target(false)
108        .try_init()
109        .map_err(|e| miette!(e))?;
110
111    Ok(())
112}
113
114fn print_completion(bin_name: &str, shell: &str) {
115    fn print<G>(bin_name: &str, g: G)
116    where
117        G: Generator,
118    {
119        clap_complete::generate(g, &mut App::command(), bin_name, &mut io::stdout());
120    }
121    match shell {
122        "bash" => print(bin_name, Shell::Bash),
123        "elvish" => print(bin_name, Shell::Elvish),
124        "fish" => print(bin_name, Shell::Fish),
125        "powershell" => print(bin_name, Shell::PowerShell),
126        "zsh" => print(bin_name, Shell::Zsh),
127        "nushell" => print(bin_name, clap_complete_nushell::Nushell),
128        _ => panic!(
129            "error: unknown shell `{shell}`, expected one of `bash`, `elvish`, `fish`, `powershell`, `zsh`, `nushell`"
130        ),
131    }
132}
133
134fn generate_man(output_dir: &str) {
135    clap_mangen::generate_to(App::command(), output_dir).unwrap();
136}