use clap::{Parser, Subcommand};
mod ask;
mod example;
#[derive(Parser)]
#[command(author, version, about, long_about = None, arg_required_else_help = true)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Ask {
question: String,
default: Option<String>,
#[arg(short, long)]
json: bool,
#[arg(
long,
help = "Allow the user input to be blank, otherwise re-ask again"
)]
allow_blank: bool,
#[arg(
long,
value_name = "JSON_ARRAY",
help = "JSON serialized array of autocompletion strings to allow",
default_value = "[]"
)]
suggestions: Option<String>,
},
Confirm {
question: String,
default: Option<ask::Confirmation>,
#[arg(short, long, help = "when canceling, use this exit code instead of 1")]
cancel_code: Option<u8>,
},
Choose {
question: String,
options: Vec<String>,
#[arg(short, long, value_name = "ITEM")]
default: Option<String>,
#[arg(short, long)]
json: bool,
#[arg(short, long, help = "return result as numeric value")]
numeric: bool,
#[arg(short, long, help = "when canceling, use this exit code instead of 1")]
cancel_code: Option<u8>,
},
Select {
question: String,
options: Vec<String>,
#[arg(short, long, value_name = "JSON_ARRAY")]
default: Option<String>,
#[arg(short, long)]
json: bool,
#[arg(short, long, help = "when canceling, use this exit code instead of 1")]
cancel_code: Option<u8>,
},
Date {
question: String,
#[arg(default_value = "%Y-%m-%d", long, value_name = "FORMAT")]
format: Option<String>,
#[arg(long, value_name = "DATE")]
default: Option<String>,
#[arg(long, value_name = "DATE")]
min_date: Option<String>,
#[arg(long, value_name = "DATE")]
max_date: Option<String>,
#[arg(long, value_name = "DATE")]
starting_date: Option<String>,
#[arg(default_value = "sunday", long, value_name = "WEEKDAY")]
week_start: Option<chrono::Weekday>,
#[arg(long, value_name = "MESSAGE")]
help_message: Option<String>,
#[arg(short, long)]
json: bool,
},
Editor {
message: String,
#[arg(long, value_name = "TEXT")]
default: Option<String>,
#[arg(long, value_name = "MESSAGE")]
help_message: Option<String>,
#[arg(long, value_name = "EXTENSION")]
file_extension: Option<String>,
#[arg(short, long)]
json: bool,
},
Menu {
#[arg(value_name = "Menu Heading")]
heading: String,
#[arg(value_name = "Entry = command")]
entries: Vec<String>,
#[arg(short, long, value_name = "ENTRY")]
default: Option<String>,
#[arg(long)]
once: bool,
#[arg(short, long, help = "when canceling, use this exit code instead of 0")]
cancel_code: Option<u8>,
},
Example {
name: Option<String>,
},
}
fn program() -> Result<u8, u8> {
let cli = Cli::parse();
match &cli.command {
Some(Commands::Ask {
question,
default,
json,
allow_blank,
suggestions,
}) => {
let response = ask::ask!(
question,
default.clone().unwrap_or(String::from("")).as_str(),
*allow_blank,
suggestions.clone().unwrap_or("".to_string()).as_str()
);
if *json {
println!(
"{}",
serde_json::to_string(&response).unwrap_or("".to_string())
)
} else {
println!("{}", response);
}
Ok(0)
}
Some(Commands::Confirm {
question,
default,
cancel_code,
}) => match ask::confirm(question, default.clone(), cancel_code.unwrap_or(1)) {
true => Ok(0),
false => Err(1),
},
Some(Commands::Choose {
question,
options,
default,
json,
numeric,
cancel_code,
}) => {
let choice = ask::choose(
question,
default.clone().unwrap_or(String::from("")).as_str(),
options.iter().map(String::as_str).collect(),
numeric,
cancel_code.unwrap_or(1),
);
if *json {
println!(
"{}",
serde_json::to_string(&choice).unwrap_or("".to_string())
)
} else {
println!("{}", choice);
}
Ok(0)
}
Some(Commands::Select {
question,
options,
default,
json,
cancel_code,
}) => {
let selections = ask::select(
question,
default.clone().unwrap_or("".to_string()).as_str(),
options.iter().map(String::as_str).collect(),
cancel_code.unwrap_or(1),
);
if *json {
println!(
"{}",
serde_json::to_string(&selections).unwrap_or("".to_string())
)
} else {
for t in selections {
println!("{}", t);
}
}
Ok(0)
}
Some(Commands::Date {
question,
default,
min_date,
max_date,
starting_date,
week_start,
help_message,
format,
json,
}) => {
let date = ask::date(
question,
default.clone().unwrap_or("".to_string()).as_str(),
min_date.clone().unwrap_or("".to_string()).as_str(),
max_date.clone().unwrap_or("".to_string()).as_str(),
starting_date.clone().unwrap_or("".to_string()).as_str(),
week_start.clone().unwrap_or(chrono::Weekday::Sun),
help_message.clone().unwrap_or("".to_string()).as_str(),
format.clone().unwrap_or("".to_string()).as_str(),
);
if *json {
println!("{}", serde_json::to_string(&date).unwrap_or("".to_string()))
} else {
println!("{}", date);
}
Ok(0)
}
Some(Commands::Editor {
message,
default,
help_message,
file_extension,
json,
}) => {
let text = ask::editor(
message,
default.clone().unwrap_or("".to_string()).as_str(),
help_message.clone().unwrap_or("".to_string()).as_str(),
file_extension.clone().unwrap_or("".to_string()).as_str(),
);
if *json {
println!("{}", serde_json::to_string(&text).unwrap())
} else {
println!("{}", text);
}
Ok(0)
}
Some(Commands::Example { name }) => {
let n = name.clone().unwrap_or("".to_string());
match example::example(&n) {
Ok(name) => {
println!("{}", name);
Ok(0)
}
Err(e) => {
eprintln!("{:?}", e);
Err(1)
}
}
}
Some(Commands::Menu {
heading,
entries,
default,
once,
cancel_code
}) => match ask::menu(heading, entries, default, once, cancel_code.unwrap_or(0)) {
Ok(2) => Err(2),
Ok(_) => Ok(0),
Err(_) => Err(1),
},
None => Err(1),
}
}
fn main() {
match program() {
Ok(_) => std::process::exit(0),
Err(code) => std::process::exit(code.into()),
};
}