// SPDX-FileCopyrightText: 2022 Matteo Settenvini // SPDX-License-Identifier: AGPL-3.0-or-later mod api_client; mod commands; mod config; mod constants; mod event; mod helpers; mod recipe; use { crate::api_client::ApiClient, crate::config::Config, anyhow::Result, clap::{arg, command, ArgMatches, Command}, std::path::PathBuf, }; #[tokio::main(flavor = "multi_thread")] async fn main() -> Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); let args = setup_args(); parse_args(&args).await } fn setup_args() -> ArgMatches { let server_arg = arg!(-s --server "NextCloud server to connect to").required(false); command!() .propagate_version(true) .subcommand_required(true) .arg_required_else_help(true) .subcommand( Command::new("init") .about("Authenticate against the provided NextCloud server") .arg(arg!( "NextCloud server to connect to")), ) .subcommand( Command::new("import") .about("Import the given URLs into NextCloud's cookbook") .arg(server_arg.clone()) .arg(arg!( ... "One or more URLs each pointing to page with a recipe to import in NextCloud")), ) .subcommand( Command::new("groceries") .about("Create a grocery list from scheduled calendar events") .arg(server_arg.clone()) .arg(arg!( "The name of the calendar to read from for scheduled recipes")) .arg(arg!( "The relative path for the Markdown file that will be saved on the server. Recommended ending it in '.md'.")) .arg(arg!(-d --days "Number of days in the future to consider") .value_parser(clap::builder::RangedU64ValueParser::::new().range(1..)) .required(false) .default_value("7")) ) .subcommand( Command::new("schedule") .about("TODO; not implemented yet (pick automatically recipes and create calendar events for them)") .arg(server_arg.clone()) .arg(arg!(-d --days "Number of days to schedule") .value_parser(clap::builder::RangedU64ValueParser::::new().range(1..)) .required(false) .default_value("7")) ) .subcommand( Command::new("schedule-csv") .about("TEMPORARY WIP FUNCTION USED FOR INTERNAL TESTING") .arg(server_arg.clone()) .arg(arg!( "")) .arg(arg!( "").value_parser(clap::value_parser!(PathBuf))) ) .subcommand( Command::new("purge") .about("Removes all events created by this application from a given calendar") .arg(server_arg.clone()) .arg(arg!( "")) ) .get_matches() } async fn parse_args(args: &ArgMatches) -> Result<()> { let mut configuration = Config::new(); match args.subcommand() { Some(("init", sub_matches)) => { let server = sub_matches .get_one::("server") .expect("Mandatory parameter "); commands::init::with(&mut configuration, server).await } Some(("import", sub_matches)) => { let api_client = get_api_client(&sub_matches, &configuration)?; let urls = sub_matches .get_many::("url") .expect("At least one url is required") .map(|s| s.as_str()); commands::import::with(&api_client, urls).await } Some(("groceries", sub_matches)) => { let api_client = get_api_client(&sub_matches, &configuration)?; let calendar_name = sub_matches .get_one::("calendar_name") .expect(" is a mandatory parameter, it cannot be missing"); let days = sub_matches.get_one::("days").unwrap(); let filename = sub_matches.get_one::("filename").unwrap(); commands::groceries::with(&api_client, calendar_name.as_str(), filename, *days).await } Some(("schedule", sub_matches)) => { let api_client = get_api_client(&sub_matches, &configuration)?; commands::schedule::with(&api_client).await } Some(("schedule-csv", sub_matches)) => { let api_client = get_api_client(&sub_matches, &configuration)?; let csv_file = sub_matches .get_one::("csv_file") .expect(" is a mandatory parameter, it cannot be missing"); let calendar_name = sub_matches .get_one::("calendar_name") .expect(" is a mandatory parameter, it cannot be missing"); commands::schedule_csv::with(&api_client, calendar_name.as_str(), &csv_file).await } Some(("purge", sub_matches)) => { let api_client = get_api_client(&sub_matches, &configuration)?; let calendar_name = sub_matches .get_one::("calendar_name") .expect(" is a mandatory parameter, it cannot be missing"); commands::purge::with(&api_client, calendar_name.as_str()).await } _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), } } fn get_api_client(sub_matches: &ArgMatches, configuration: &Config) -> Result { let server_name = sub_matches .get_one::("server") .unwrap_or_else(|| { let servers = &configuration.credentials.servers; match servers.len() { 0 => panic!("No NextCloud server set up yet, use '{} init' first", env!("CARGO_BIN_NAME")), 1 => servers.iter().next().unwrap().0, _ => panic!("More than one NextCloud server set up, use the '--server' option to specify which one to use. Known servers: {:#?}", servers.keys().collect::>()), } }); ApiClient::new(&server_name, &configuration) }