Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
128 lines
3.4 KiB
Rust
128 lines
3.4 KiB
Rust
// Cuddle actions is a two part action, it is called from cuddle itself, second the cli uses a provided sdk to expose functionality
|
|
|
|
use std::{collections::BTreeMap, ffi::OsString, io::Write};
|
|
|
|
use serde::Serialize;
|
|
|
|
pub use cuddle_actions_sdk_derive::Actions;
|
|
|
|
// Fix design make it so that it works like axum!
|
|
|
|
type ActionFn = dyn Fn() -> anyhow::Result<()> + 'static;
|
|
|
|
struct Action {
|
|
name: String,
|
|
description: Option<String>,
|
|
f: Box<ActionFn>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize)]
|
|
struct ActionSchema<'a> {
|
|
name: &'a str,
|
|
description: Option<&'a str>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize)]
|
|
struct ActionsSchema<'a> {
|
|
actions: Vec<ActionSchema<'a>>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct CuddleActions {
|
|
actions: BTreeMap<String, Action>,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct AddActionOptions {
|
|
description: Option<String>,
|
|
}
|
|
|
|
impl CuddleActions {
|
|
pub fn add_action<F>(
|
|
&mut self,
|
|
name: &str,
|
|
action_fn: F,
|
|
options: &AddActionOptions,
|
|
) -> &mut Self
|
|
where
|
|
F: Fn() -> anyhow::Result<()> + 'static,
|
|
{
|
|
self.actions.insert(
|
|
name.into(),
|
|
Action {
|
|
name: name.into(),
|
|
description: options.description.clone(),
|
|
f: Box::new(action_fn),
|
|
},
|
|
);
|
|
|
|
self
|
|
}
|
|
|
|
pub fn execute(&mut self) -> anyhow::Result<()> {
|
|
self.execute_from(std::env::args())?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn execute_from<I, T>(&mut self, items: I) -> anyhow::Result<()>
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
let mut do_cmd = clap::Command::new("do").subcommand_required(true);
|
|
|
|
for action in self.actions.values() {
|
|
let mut do_action_cmd = clap::Command::new(action.name.clone());
|
|
if let Some(description) = &action.description {
|
|
do_action_cmd = do_action_cmd.about(description.clone());
|
|
}
|
|
|
|
do_cmd = do_cmd.subcommand(do_action_cmd);
|
|
}
|
|
|
|
let root = clap::Command::new("cuddle-action")
|
|
.subcommand_required(true)
|
|
.subcommand(clap::Command::new("schema"))
|
|
.subcommand(do_cmd);
|
|
|
|
let matches = root.try_get_matches_from(items)?;
|
|
match matches.subcommand().expect("subcommand to be required") {
|
|
("schema", _args) => {
|
|
let output = self.get_pretty_actions()?;
|
|
|
|
// Write all stdout to buffer
|
|
std::io::stdout().write_all(output.as_bytes())?;
|
|
std::io::stdout().write_all("\n".as_bytes())?;
|
|
|
|
Ok(())
|
|
}
|
|
("do", args) => {
|
|
let (command_name, _args) = args.subcommand().unwrap();
|
|
match self.actions.get_mut(command_name) {
|
|
Some(action) => (*action.f)(),
|
|
None => {
|
|
anyhow::bail!("command not found: {}", command_name);
|
|
}
|
|
}
|
|
}
|
|
_ => anyhow::bail!("no command found"),
|
|
}
|
|
}
|
|
|
|
pub fn get_pretty_actions(&self) -> anyhow::Result<String> {
|
|
let schema = self
|
|
.actions
|
|
.values()
|
|
.map(|a| ActionSchema {
|
|
name: &a.name,
|
|
description: a.description.as_deref(),
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let output = serde_json::to_string_pretty(&ActionsSchema { actions: schema })?;
|
|
|
|
Ok(output)
|
|
}
|
|
}
|