use std::io::Write; use anyhow::anyhow; use clap::{FromArgMatches, Subcommand}; use get_command::GetCommand; use crate::{cuddle_state::Cuddle, state::ValidatedState}; mod get_command; mod init_command; pub struct Cli { cli: clap::Command, cuddle: Cuddle, } #[derive(clap::Parser, Debug)] pub enum Subcommands { Init(init_command::InitCommand), } impl Cli { pub fn new(cuddle: Cuddle) -> Self { let cli = clap::Command::new("cuddle").subcommand_required(true); Self { cli, cuddle } } pub async fn setup(mut self) -> anyhow::Result { let commands = self.get_commands().await?; self.cli = self.cli.subcommands(commands); self.cli = Subcommands::augment_subcommands(self.cli); // TODO: Add global // TODO: Add components Ok(self) } async fn get_commands(&self) -> anyhow::Result> { let actions = self .cuddle .state .actions .actions .iter() .map(|a| clap::Command::new(&a.name)) .collect::>(); Ok(vec![ clap::Command::new("do") .subcommand_required(true) .subcommands(actions.as_slice()), clap::Command::new("get") .about(GetCommand::description()) .arg( clap::Arg::new("query") .required(true) .help("query is how values are extracted, '.project.name' etc"), ), ]) } pub async fn execute(self) -> anyhow::Result<()> { let matches = self.cli.get_matches_from(std::env::args()); if let Ok(subcommand) = Subcommands::from_arg_matches(&matches) { match subcommand { Subcommands::Init(init_command) => { init_command.execute(&self.cuddle).await?; } } } match matches .subcommand() .ok_or(anyhow::anyhow!("failed to find subcommand"))? { ("do", args) => { tracing::debug!("executing do"); let (action_name, args) = args .subcommand() .ok_or(anyhow::anyhow!("failed to find do subcommand"))?; let action = self .cuddle .state .actions .actions .iter() .find(|a| a.name == action_name) .ok_or(anyhow::anyhow!("failed to find {}", action_name))?; action.call().await?; } ("get", args) => { if !self.cuddle.has_project() { anyhow::bail!("get is not available without a project"); } let query = args .get_one::("query") .ok_or(anyhow!("query is required"))?; let res = GetCommand::new(self.cuddle).execute(query).await?; std::io::stdout().write_all(res.as_bytes())?; std::io::stdout().write_all("\n".as_bytes())?; } _ => {} } Ok(()) } async fn add_project_commands(&self) -> anyhow::Result> { if let Some(_project) = self.cuddle.state.project.as_ref() { // Add project level commands return Ok(vec![]); } Ok(Vec::new()) } async fn add_plan_commands(self) -> anyhow::Result { if let Some(_plan) = self.cuddle.state.plan.as_ref() { // Add plan level commands } Ok(self) } }