93
crates/cuddle/src/actions.rs
Normal file
93
crates/cuddle/src/actions.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use rust_builder::RustActionsBuilder;
|
||||
|
||||
use crate::state::validated_project::Value;
|
||||
|
||||
pub mod builder;
|
||||
|
||||
pub mod rust_builder {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::ExecutableActions;
|
||||
|
||||
pub struct RustActionsBuilder {
|
||||
root_path: PathBuf,
|
||||
}
|
||||
|
||||
impl RustActionsBuilder {
|
||||
pub fn new(root_path: PathBuf) -> Self {
|
||||
Self { root_path }
|
||||
}
|
||||
|
||||
pub async fn build(&self) -> anyhow::Result<ExecutableActions> {
|
||||
Ok(ExecutableActions::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Actions {
|
||||
variants: Vec<ActionVariant>,
|
||||
}
|
||||
|
||||
impl Actions {
|
||||
pub async fn new(path: &Path, value: &Value) -> anyhow::Result<Option<Self>> {
|
||||
let Some(project) = value.get(&["project", "actions"]) else {
|
||||
tracing::debug!("found no actions folder");
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut variants = Vec::default();
|
||||
if let Some(Value::Bool(true)) = project.get(&["rust"]) {
|
||||
tracing::debug!("rust actions enabled");
|
||||
variants.push(ActionVariant::Rust {
|
||||
root_path: path.to_path_buf(),
|
||||
});
|
||||
} else {
|
||||
tracing::trace!("rust actions not enabled");
|
||||
}
|
||||
|
||||
Ok(Some(Self { variants }))
|
||||
}
|
||||
|
||||
pub async fn build(&mut self) -> anyhow::Result<Vec<ExecutableActions>> {
|
||||
let mut executable_actions = Vec::default();
|
||||
|
||||
for variant in &mut self.variants {
|
||||
match variant {
|
||||
ActionVariant::Rust { root_path } => {
|
||||
let actions = RustActionsBuilder::new(root_path.clone()).build().await?;
|
||||
|
||||
executable_actions.push(actions);
|
||||
}
|
||||
ActionVariant::Docker => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(executable_actions)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ActionVariant {
|
||||
Rust { root_path: PathBuf },
|
||||
Docker,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ExecutableActions {
|
||||
actions: Vec<ExecutableAction>,
|
||||
}
|
||||
|
||||
pub struct ExecutableAction {
|
||||
name: String,
|
||||
description: String,
|
||||
flags: BTreeMap<String, ExecutableActionFlag>,
|
||||
}
|
||||
|
||||
pub enum ExecutableActionFlag {
|
||||
String,
|
||||
Bool,
|
||||
}
|
||||
1
crates/cuddle/src/actions/builder.rs
Normal file
1
crates/cuddle/src/actions/builder.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub struct ActionsBuilder {}
|
||||
@@ -1,17 +1,24 @@
|
||||
use std::{borrow::BorrowMut, io::Write};
|
||||
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<ValidatedState>,
|
||||
}
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
pub enum Subcommands {
|
||||
Init(init_command::InitCommand),
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new(cuddle: Cuddle<ValidatedState>) -> Self {
|
||||
let cli = clap::Command::new("cuddle").subcommand_required(true);
|
||||
@@ -24,6 +31,8 @@ impl Cli {
|
||||
|
||||
self.cli = self.cli.subcommands(commands);
|
||||
|
||||
self.cli = Subcommands::augment_subcommands(self.cli);
|
||||
|
||||
// TODO: Add global
|
||||
// TODO: Add components
|
||||
|
||||
@@ -44,9 +53,17 @@ impl Cli {
|
||||
}
|
||||
|
||||
pub async fn execute(self) -> anyhow::Result<()> {
|
||||
match self
|
||||
.cli
|
||||
.get_matches_from(std::env::args())
|
||||
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"))?
|
||||
{
|
||||
|
||||
31
crates/cuddle/src/cli/init_command.rs
Normal file
31
crates/cuddle/src/cli/init_command.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use clap::Parser;
|
||||
use rust_action::RustAction;
|
||||
|
||||
use crate::{cuddle_state::Cuddle, state::ValidatedState};
|
||||
|
||||
pub mod rust_action;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(subcommand_required = true)]
|
||||
pub struct InitCommand {
|
||||
#[clap(subcommand)]
|
||||
commands: InitCommands,
|
||||
}
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
pub enum InitCommands {
|
||||
#[clap(name = "actions:rust")]
|
||||
RustAction(RustAction),
|
||||
}
|
||||
|
||||
impl InitCommand {
|
||||
pub async fn execute(&self, cuddle: &Cuddle<ValidatedState>) -> anyhow::Result<()> {
|
||||
match &self.commands {
|
||||
InitCommands::RustAction(rust_action) => {
|
||||
rust_action.execute(cuddle).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
13
crates/cuddle/src/cli/init_command/rust_action.rs
Normal file
13
crates/cuddle/src/cli/init_command/rust_action.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use crate::{cuddle_state::Cuddle, state::ValidatedState};
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
pub struct RustAction {}
|
||||
|
||||
impl RustAction {
|
||||
pub async fn execute(&self, cuddle: &Cuddle<ValidatedState>) -> anyhow::Result<()> {
|
||||
let project = cuddle.must_project()?;
|
||||
let root = &project.root;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::plan::{ClonedPlan, Plan};
|
||||
use crate::project::ProjectPlan;
|
||||
use crate::state::validated_project::Project;
|
||||
use crate::state::{self, ValidatedState};
|
||||
|
||||
pub struct Start {}
|
||||
@@ -54,7 +55,7 @@ impl Cuddle<PrepareProject> {
|
||||
|
||||
impl Cuddle<PreparePlan> {
|
||||
pub async fn build_state(&self) -> anyhow::Result<Cuddle<ValidatedState>> {
|
||||
let state = if let Some(project) = &self.state.project {
|
||||
let mut state = if let Some(project) = &self.state.project {
|
||||
let state = state::State::new();
|
||||
let raw_state = state.build_state(project, &self.state.plan).await?;
|
||||
|
||||
@@ -63,6 +64,8 @@ impl Cuddle<PreparePlan> {
|
||||
ValidatedState::default()
|
||||
};
|
||||
|
||||
state.build_actions().await?;
|
||||
|
||||
Ok(Cuddle { state })
|
||||
}
|
||||
}
|
||||
@@ -79,4 +82,13 @@ impl Cuddle<ValidatedState> {
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn project(&self) -> Option<&Project> {
|
||||
self.state.project.as_ref()
|
||||
}
|
||||
|
||||
pub fn must_project(&self) -> anyhow::Result<&Project> {
|
||||
self.project()
|
||||
.ok_or(anyhow::anyhow!("project is not available in this context"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use cli::Cli;
|
||||
use cuddle_state::Cuddle;
|
||||
|
||||
mod actions;
|
||||
mod cli;
|
||||
mod cuddle_state;
|
||||
mod plan;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use validated_project::Project;
|
||||
|
||||
use crate::{
|
||||
actions::Actions,
|
||||
plan::{self, ClonedPlan, PlanPathExt},
|
||||
project::{self, ProjectPlan},
|
||||
schema_validator::SchemaValidator,
|
||||
@@ -42,6 +43,7 @@ impl State {
|
||||
Ok(ValidatedState {
|
||||
project: Some(project),
|
||||
plan: None,
|
||||
actions: LocalActions::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -55,6 +57,51 @@ pub struct RawState {
|
||||
pub struct ValidatedState {
|
||||
pub project: Option<Project>,
|
||||
pub plan: Option<Plan>,
|
||||
|
||||
pub actions: LocalActions,
|
||||
}
|
||||
|
||||
impl ValidatedState {
|
||||
pub(crate) async fn build_actions(&mut self) -> anyhow::Result<&mut Self> {
|
||||
tracing::debug!("building actions");
|
||||
|
||||
if let Some(project) = &self.project {
|
||||
if let Some(actions) = Actions::new(&project.root, &project.value).await? {
|
||||
self.actions.add(actions);
|
||||
}
|
||||
}
|
||||
|
||||
self.actions.build().await?;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Plan {}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LocalActions(Vec<Actions>);
|
||||
|
||||
impl LocalActions {
|
||||
pub fn add(&mut self, actions: Actions) -> &mut Self {
|
||||
self.0.push(actions);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn build(&mut self) -> anyhow::Result<&mut Self> {
|
||||
for actions in &mut self.0 {
|
||||
actions.build().await?;
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for LocalActions {
|
||||
type Target = Vec<Actions>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,3 +73,18 @@ impl From<&toml::Value> for Value {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn get(&self, path: &[&str]) -> Option<&Value> {
|
||||
match path.split_first() {
|
||||
Some((current, rest)) => match self {
|
||||
Value::Map(map) => match map.get(¤t.to_string()) {
|
||||
Some(value) => value.get(rest),
|
||||
None => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
None => Some(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user