feat: add actions sdk

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2024-08-26 09:05:58 +02:00
parent 02dd805db4
commit 186f13a16c
7 changed files with 177 additions and 3 deletions

View File

@@ -0,0 +1,13 @@
[package]
name = "cuddle-actions"
edition = "2021"
version.workspace = true
[dependencies]
anyhow.workspace = true
clap.workspace = true
serde.workspace = true
serde_json.workspace = true
[dev-dependencies]
pretty_assertions = "1.4.0"

View File

@@ -0,0 +1,93 @@
// 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;
// Fix design make it so that it works like axum!
type ActionFn = dyn Fn() + '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(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() + '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<()> {
let output = 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(())
}
pub fn execute_from<I, T>(&mut self, items: I) -> anyhow::Result<String>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let root = clap::Command::new("cuddle-action")
.subcommand_required(true)
.subcommand(clap::Command::new("schema"));
let matches = root.get_matches_from(items);
match matches.subcommand().expect("subcommand to be required") {
("schema", _args) => {
let schema = self
.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)?;
Ok(output)
}
_ => Ok("".into()),
}
}
}

View File

@@ -0,0 +1,31 @@
use cuddle_actions::AddActionOptions;
use pretty_assertions::assert_eq;
#[test]
fn test_can_schema_no_actions() -> anyhow::Result<()> {
let output =
cuddle_actions::CuddleActions::default().execute_from(vec!["cuddle-actions", "schema"])?;
assert_eq!("[]", &output);
Ok(())
}
#[test]
fn test_can_schema_simple_action() -> anyhow::Result<()> {
let output = cuddle_actions::CuddleActions::default()
.add_action("something", || {}, &AddActionOptions::default())
.execute_from(vec!["cuddle-actions", "schema"])?;
assert_eq!(
r#"[
{
"name": "something",
"description": null
}
]"#,
&output
);
Ok(())
}

View File

@@ -11,9 +11,9 @@ tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
serde.workspace = true
serde_json.workspace = true
uuid.workspace = true
serde = { version = "1.0.197", features = ["derive"] }
uuid = { version = "1.7.0", features = ["v4"] }
toml = "0.8.19"
fs_extra = "1.3.0"
serde_json = "1.0.127"

View File

@@ -0,0 +1 @@
{"ProjectSchema":{"fields":{"name":{"fields":null,"type":null,"contracts":["String"],"documentation":null}},"type":null,"contracts":[],"documentation":null}}