feat: working cuddle actions,
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
although I am not entirely happy with it, as we're missing args, state and project variables Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
186f13a16c
commit
62a677ffcd
@ -5,7 +5,7 @@ version.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clap.workspace = true
|
clap = { workspace = true, features = ["string"] }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use serde::Serialize;
|
|||||||
|
|
||||||
// Fix design make it so that it works like axum!
|
// Fix design make it so that it works like axum!
|
||||||
|
|
||||||
type ActionFn = dyn Fn() + 'static;
|
type ActionFn = dyn Fn() -> anyhow::Result<()> + 'static;
|
||||||
|
|
||||||
struct Action {
|
struct Action {
|
||||||
name: String,
|
name: String,
|
||||||
@ -38,7 +38,7 @@ impl CuddleActions {
|
|||||||
options: &AddActionOptions,
|
options: &AddActionOptions,
|
||||||
) -> &mut Self
|
) -> &mut Self
|
||||||
where
|
where
|
||||||
F: Fn() + 'static,
|
F: Fn() -> anyhow::Result<()> + 'static,
|
||||||
{
|
{
|
||||||
self.actions.insert(
|
self.actions.insert(
|
||||||
name.into(),
|
name.into(),
|
||||||
@ -53,41 +53,68 @@ impl CuddleActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&mut self) -> anyhow::Result<()> {
|
pub fn execute(&mut self) -> anyhow::Result<()> {
|
||||||
let output = self.execute_from(std::env::args())?;
|
self.execute_from(std::env::args())?;
|
||||||
|
|
||||||
// Write all stdout to buffer
|
|
||||||
std::io::stdout().write_all(output.as_bytes())?;
|
|
||||||
std::io::stdout().write_all("\n".as_bytes())?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_from<I, T>(&mut self, items: I) -> anyhow::Result<String>
|
pub fn execute_from<I, T>(&mut self, items: I) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = T>,
|
I: IntoIterator<Item = T>,
|
||||||
T: Into<OsString> + Clone,
|
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")
|
let root = clap::Command::new("cuddle-action")
|
||||||
.subcommand_required(true)
|
.subcommand_required(true)
|
||||||
.subcommand(clap::Command::new("schema"));
|
.subcommand(clap::Command::new("schema"))
|
||||||
|
.subcommand(do_cmd);
|
||||||
|
|
||||||
let matches = root.get_matches_from(items);
|
let matches = root.try_get_matches_from(items)?;
|
||||||
match matches.subcommand().expect("subcommand to be required") {
|
match matches.subcommand().expect("subcommand to be required") {
|
||||||
("schema", _args) => {
|
("schema", _args) => {
|
||||||
let schema = self
|
let output = self.get_pretty_actions()?;
|
||||||
.actions
|
|
||||||
.values()
|
|
||||||
.map(|a| ActionSchema {
|
|
||||||
name: &a.name,
|
|
||||||
description: a.description.as_ref().map(|d| d.as_str()),
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let output = serde_json::to_string_pretty(&schema)?;
|
// Write all stdout to buffer
|
||||||
|
std::io::stdout().write_all(output.as_bytes())?;
|
||||||
|
std::io::stdout().write_all("\n".as_bytes())?;
|
||||||
|
|
||||||
Ok(output)
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Ok("".into()),
|
("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(&schema)?;
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,7 @@ use pretty_assertions::assert_eq;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_can_schema_no_actions() -> anyhow::Result<()> {
|
fn test_can_schema_no_actions() -> anyhow::Result<()> {
|
||||||
let output =
|
let output = cuddle_actions::CuddleActions::default().get_pretty_actions()?;
|
||||||
cuddle_actions::CuddleActions::default().execute_from(vec!["cuddle-actions", "schema"])?;
|
|
||||||
|
|
||||||
assert_eq!("[]", &output);
|
assert_eq!("[]", &output);
|
||||||
|
|
||||||
@ -14,8 +13,8 @@ fn test_can_schema_no_actions() -> anyhow::Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_can_schema_simple_action() -> anyhow::Result<()> {
|
fn test_can_schema_simple_action() -> anyhow::Result<()> {
|
||||||
let output = cuddle_actions::CuddleActions::default()
|
let output = cuddle_actions::CuddleActions::default()
|
||||||
.add_action("something", || {}, &AddActionOptions::default())
|
.add_action("something", || Ok(()), &AddActionOptions::default())
|
||||||
.execute_from(vec!["cuddle-actions", "schema"])?;
|
.get_pretty_actions()?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
r#"[
|
r#"[
|
||||||
@ -29,3 +28,40 @@ fn test_can_schema_simple_action() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_can_call_simple_action() -> anyhow::Result<()> {
|
||||||
|
cuddle_actions::CuddleActions::default()
|
||||||
|
.add_action("something", || Ok(()), &AddActionOptions::default())
|
||||||
|
.execute_from(vec!["cuddle-actions", "do", "something"])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_can_fail_on_unknown_command() -> anyhow::Result<()> {
|
||||||
|
let res = cuddle_actions::CuddleActions::default().execute_from(vec![
|
||||||
|
"cuddle-actions",
|
||||||
|
"do",
|
||||||
|
"something",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_can_cmd_can_fail() -> anyhow::Result<()> {
|
||||||
|
let res = cuddle_actions::CuddleActions::default()
|
||||||
|
.add_action(
|
||||||
|
"something",
|
||||||
|
|| anyhow::bail!("failed to run cmd"),
|
||||||
|
&AddActionOptions::default(),
|
||||||
|
)
|
||||||
|
.execute_from(vec!["cuddle-actions", "do", "something"]);
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user