use std::io::IsTerminal;
use std::path::PathBuf;
use std::str::FromStr;
use anyhow::Context;
use clap::Parser;
use rustc_version::Channel;
use cargo_wizard::{
PredefinedTemplateKind, Profile, WizardOptions, parse_workspace, resolve_manifest_path,
};
use crate::cli::CliConfig;
use crate::dialog::{
DialogError, KnownCargoOptions, on_template_applied, profile_from_str, run_root_dialog,
};
mod cli;
mod dialog;
#[derive(clap::Parser, Debug)]
#[clap(author, version, about)]
#[clap(bin_name("cargo"))]
#[clap(disable_help_subcommand(true))]
enum Args {
#[clap(author, version, about)]
Wizard(InnerArgs),
}
#[derive(clap::ValueEnum, Clone, Debug)]
enum ColorPolicy {
Auto,
Always,
Never,
}
#[derive(clap::ValueEnum, Clone, Debug)]
enum NightlyOptions {
Auto,
On,
Off,
}
#[derive(clap::Parser, Debug)]
struct InnerArgs {
#[arg(
long,
value_enum,
default_value_t = ColorPolicy::Auto,
global = true,
help_heading("GLOBAL OPTIONS"),
hide_short_help(true)
)]
colors: ColorPolicy,
#[arg(
long,
value_enum,
default_value_t = NightlyOptions::Auto,
default_missing_value = "on",
global = true,
num_args = 0..=1,
require_equals = true,
help_heading("GLOBAL OPTIONS"),
hide_short_help(true)
)]
nightly: NightlyOptions,
#[clap(subcommand)]
subcmd: Option<SubCommand>,
}
#[derive(Clone, Debug)]
struct ProfileArg(Profile);
impl FromStr for ProfileArg {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
profile_from_str(s).map(ProfileArg)
}
}
#[derive(clap::Parser, Debug)]
struct ApplyArgs {
template: PredefinedTemplateKind,
profile: ProfileArg,
}
#[derive(clap::Parser, Debug)]
enum SubCommand {
Apply {
#[clap(flatten)]
args: ApplyArgs,
#[clap(long)]
manifest_path: Option<PathBuf>,
},
}
fn options_from_args(args: &InnerArgs) -> WizardOptions {
let mut options = WizardOptions::default();
let is_nightly = match args.nightly {
NightlyOptions::Auto => match rustc_version::version_meta() {
Ok(metadata) => {
matches!(metadata.channel, Channel::Nightly)
}
Err(error) => {
eprintln!(
"Cannot get compiler channel, defaulting to *no* nightly options ({error:?}"
);
false
}
},
NightlyOptions::On => true,
NightlyOptions::Off => false,
};
if is_nightly {
options = options.with_nightly_items();
}
options
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
match args {
Args::Wizard(root_args) => {
let options = options_from_args(&root_args);
let cargo_options =
KnownCargoOptions::create().context("Cannot get known Cargo options")?;
let cli_config = setup_cli(root_args.colors);
match root_args.subcmd {
Some(SubCommand::Apply {
args,
manifest_path,
}) => {
let manifest_path = match manifest_path {
Some(path) => path,
None => {
resolve_manifest_path().context("Cannot resolve Cargo.toml path")?
}
};
let workspace = parse_workspace(&manifest_path)?;
let template = args.template.build_template(&options);
let modified = workspace.apply_template(&args.profile.0, &template)?;
modified.write()?;
on_template_applied(&cargo_options, args.template, &template, &args.profile.0);
}
None => {
if let Err(error) = run_root_dialog(cli_config, cargo_options, options) {
match error {
DialogError::Interrupted => {
println!();
}
DialogError::Generic(error) => {
panic!("{error:?}");
}
}
}
}
}
}
}
Ok(())
}
fn setup_cli(policy: ColorPolicy) -> CliConfig {
let mut use_colors = match policy {
ColorPolicy::Always => true,
ColorPolicy::Auto => std::io::stdout().is_terminal(),
ColorPolicy::Never => false,
};
if std::env::var("NO_COLOR") == Ok("1".to_string()) {
use_colors = false;
}
console::set_colors_enabled(use_colors);
console::set_colors_enabled_stderr(use_colors);
CliConfig::new(use_colors)
}