All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
203 lines
5.3 KiB
Rust
203 lines
5.3 KiB
Rust
use std::{
|
|
collections::BTreeMap,
|
|
ops::{Deref, DerefMut},
|
|
};
|
|
|
|
use async_trait::async_trait;
|
|
|
|
pub struct CuddleCI {
|
|
pr_action: Vec<Box<dyn PullRequestAction + Send + Sync>>,
|
|
main_action: Vec<Box<dyn MainAction + Send + Sync>>,
|
|
release_action: Vec<Box<dyn ReleaseAction + Send + Sync>>,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Context {
|
|
store: BTreeMap<String, String>,
|
|
}
|
|
|
|
impl Deref for Context {
|
|
type Target = BTreeMap<String, String>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.store
|
|
}
|
|
}
|
|
|
|
impl DerefMut for Context {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.store
|
|
}
|
|
}
|
|
|
|
impl CuddleCI {
|
|
pub fn new(
|
|
pr: Box<dyn PullRequestAction + Send + Sync>,
|
|
main: Box<dyn MainAction + Send + Sync>,
|
|
release: Box<dyn ReleaseAction + Send + Sync>,
|
|
) -> Self {
|
|
Self {
|
|
pr_action: vec![pr],
|
|
main_action: vec![main],
|
|
release_action: vec![release],
|
|
}
|
|
}
|
|
|
|
pub fn with_pull_request<T>(&mut self, pr: &T) -> &mut Self
|
|
where
|
|
T: PullRequestAction + ToOwned + Send + Sync + 'static,
|
|
T: ToOwned<Owned = T>,
|
|
{
|
|
self.pr_action.push(Box::new(pr.to_owned()));
|
|
|
|
self
|
|
}
|
|
|
|
pub fn with_main<T>(&mut self, main: &T) -> &mut Self
|
|
where
|
|
T: MainAction + Send + Sync + 'static,
|
|
T: ToOwned<Owned = T>,
|
|
{
|
|
self.main_action.push(Box::new(main.to_owned()));
|
|
|
|
self
|
|
}
|
|
|
|
pub fn with_release<T>(&mut self, release: &T) -> &mut Self
|
|
where
|
|
T: ReleaseAction + Send + Sync + 'static,
|
|
T: ToOwned<Owned = T>,
|
|
{
|
|
self.release_action.push(Box::new(release.to_owned()));
|
|
|
|
self
|
|
}
|
|
|
|
pub async fn execute(
|
|
&mut self,
|
|
args: impl IntoIterator<Item = impl Into<String>>,
|
|
) -> eyre::Result<()> {
|
|
let matches = clap::Command::new("cuddle-ci")
|
|
.about("is a wrapper around common CI actions")
|
|
.subcommand(clap::Command::new("pr"))
|
|
.subcommand(clap::Command::new("main"))
|
|
.subcommand(clap::Command::new("release"))
|
|
.subcommand_required(true)
|
|
.try_get_matches_from(args.into_iter().map(|a| a.into()).collect::<Vec<String>>())?;
|
|
|
|
let mut context = Context::default();
|
|
|
|
match matches.subcommand() {
|
|
Some((name, args)) => match (name, args) {
|
|
("pr", _args) => {
|
|
eprintln!("starting pr validate");
|
|
for pr_action in self.pr_action.iter() {
|
|
pr_action.execute_pull_request(&mut context).await?;
|
|
}
|
|
eprintln!("finished pr validate");
|
|
}
|
|
("main", _args) => {
|
|
eprintln!("starting main validate");
|
|
for main_action in self.main_action.iter() {
|
|
main_action.execute_main(&mut context).await?;
|
|
}
|
|
eprintln!("finished main validate");
|
|
}
|
|
("release", _args) => {
|
|
eprintln!("starting release validate");
|
|
for release_action in self.release_action.iter() {
|
|
release_action.execute_release(&mut context).await?;
|
|
}
|
|
eprintln!("finished release validate");
|
|
}
|
|
(command_name, _) => {
|
|
eyre::bail!("command is not recognized: {}", command_name)
|
|
}
|
|
},
|
|
None => eyre::bail!("command required a subcommand [pr, main, release] etc."),
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Default for CuddleCI {
|
|
fn default() -> Self {
|
|
Self::new(
|
|
Box::new(DefaultPullRequestAction {}),
|
|
Box::new(DefaultMainAction {}),
|
|
Box::new(DefaultReleaseAction {}),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait PullRequestAction {
|
|
async fn execute_pull_request(&self, _ctx: &mut Context) -> eyre::Result<()> {
|
|
eprintln!("validate pull request: noop");
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub struct DefaultPullRequestAction {}
|
|
#[async_trait]
|
|
impl PullRequestAction for DefaultPullRequestAction {}
|
|
|
|
#[async_trait]
|
|
pub trait MainAction {
|
|
async fn execute_main(&self, _ctx: &mut Context) -> eyre::Result<()>;
|
|
}
|
|
|
|
pub struct DefaultMainAction {}
|
|
#[async_trait]
|
|
impl MainAction for DefaultMainAction {
|
|
async fn execute_main(&self, _ctx: &mut Context) -> eyre::Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait ReleaseAction {
|
|
async fn execute_release(&self, _ctx: &mut Context) -> eyre::Result<()> {
|
|
eprintln!("validate release: noop");
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub struct DefaultReleaseAction {}
|
|
#[async_trait]
|
|
impl ReleaseAction for DefaultReleaseAction {}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_can_call_default() -> eyre::Result<()> {
|
|
CuddleCI::default().execute(["cuddle-ci", "pr"]).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fails_on_no_command() -> eyre::Result<()> {
|
|
let res = CuddleCI::default().execute(["cuddle-ci"]).await;
|
|
|
|
assert!(res.is_err());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_fails_on_wrong_command() -> eyre::Result<()> {
|
|
let res = CuddleCI::default()
|
|
.execute(["cuddle-ci", "something"])
|
|
.await;
|
|
|
|
assert!(res.is_err());
|
|
|
|
Ok(())
|
|
}
|
|
}
|