Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
61db3da695
commit
5e88ffdbc9
68
crates/cuddle/src/cuddle_state.rs
Normal file
68
crates/cuddle/src/cuddle_state.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use crate::plan::{ClonedPlan, Plan};
|
||||||
|
use crate::project::ProjectPlan;
|
||||||
|
use crate::state::{self, ValidatedState};
|
||||||
|
|
||||||
|
pub struct Start {}
|
||||||
|
pub struct PrepareProject {
|
||||||
|
project: Option<ProjectPlan>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreparePlan {
|
||||||
|
project: Option<ProjectPlan>,
|
||||||
|
plan: Option<ClonedPlan>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cuddle<S = Start> {
|
||||||
|
pub state: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cuddle maintains the context for cuddle to use
|
||||||
|
// Stage 1 figure out which state to display
|
||||||
|
// Stage 2 prepare plan
|
||||||
|
// Stage 3 validate settings, build actions, prepare
|
||||||
|
|
||||||
|
impl Cuddle<Start> {
|
||||||
|
pub fn default() -> Self {
|
||||||
|
Self { state: Start {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn prepare_project(&self) -> anyhow::Result<Cuddle<PrepareProject>> {
|
||||||
|
let project = ProjectPlan::from_current_path().await?;
|
||||||
|
|
||||||
|
Ok(Cuddle {
|
||||||
|
state: PrepareProject { project },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cuddle<PrepareProject> {
|
||||||
|
pub async fn prepare_plan(&self) -> anyhow::Result<Cuddle<PreparePlan>> {
|
||||||
|
let plan = if let Some(project) = &self.state.project {
|
||||||
|
Plan::new().clone_from_project(project).await?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Cuddle {
|
||||||
|
state: PreparePlan {
|
||||||
|
project: self.state.project.clone(),
|
||||||
|
plan,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cuddle<PreparePlan> {
|
||||||
|
pub async fn build_state(&self) -> anyhow::Result<Cuddle<ValidatedState>> {
|
||||||
|
let state = if let Some(project) = &self.state.project {
|
||||||
|
let state = state::State::new();
|
||||||
|
let raw_state = state.build_state(project, &self.state.plan).await?;
|
||||||
|
|
||||||
|
state.validate_state(&raw_state).await?
|
||||||
|
} else {
|
||||||
|
ValidatedState::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Cuddle { state })
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use plan::{ClonedPlan, Plan};
|
use cuddle_state::Cuddle;
|
||||||
use project::ProjectPlan;
|
use state::{validated_project::Project, ValidatedState};
|
||||||
use state::ValidatedState;
|
|
||||||
|
|
||||||
|
mod cuddle_state;
|
||||||
mod plan;
|
mod plan;
|
||||||
mod project;
|
mod project;
|
||||||
mod schema_validator;
|
mod schema_validator;
|
||||||
@ -12,7 +12,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let _cuddle = Cuddle::default()
|
let cuddle = Cuddle::default()
|
||||||
.prepare_project()
|
.prepare_project()
|
||||||
.await?
|
.await?
|
||||||
.prepare_plan()
|
.prepare_plan()
|
||||||
@ -20,71 +20,76 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
.build_state()
|
.build_state()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
Cli::new(cuddle).setup().await?.execute().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Start {}
|
pub struct Cli {
|
||||||
struct PrepareProject {
|
cli: clap::Command,
|
||||||
project: Option<ProjectPlan>,
|
cuddle: Cuddle<ValidatedState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PreparePlan {
|
impl Cli {
|
||||||
project: Option<ProjectPlan>,
|
pub fn new(cuddle: Cuddle<ValidatedState>) -> Self {
|
||||||
plan: Option<ClonedPlan>,
|
let cli = clap::Command::new("cuddle").subcommand_required(true);
|
||||||
}
|
|
||||||
|
|
||||||
struct Cuddle<S = Start> {
|
Self { cli, cuddle }
|
||||||
state: S,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cuddle maintains the context for cuddle to use
|
|
||||||
// Stage 1 figure out which state to display
|
|
||||||
// Stage 2 prepare plan
|
|
||||||
// Stage 3 validate settings, build actions, prepare
|
|
||||||
impl Cuddle {}
|
|
||||||
|
|
||||||
impl Cuddle<Start> {
|
|
||||||
pub fn default() -> Self {
|
|
||||||
Self { state: Start {} }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn prepare_project(&self) -> anyhow::Result<Cuddle<PrepareProject>> {
|
pub async fn setup(mut self) -> anyhow::Result<Self> {
|
||||||
let project = ProjectPlan::from_current_path().await?;
|
let s = self
|
||||||
|
.add_default()
|
||||||
|
.await?
|
||||||
|
.add_project_commands()
|
||||||
|
.await?
|
||||||
|
.add_plan_commands()
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(Cuddle {
|
// TODO: Add global
|
||||||
state: PrepareProject { project },
|
// TODO: Add components
|
||||||
})
|
|
||||||
}
|
Ok(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cuddle<PrepareProject> {
|
pub async fn execute(mut self) -> anyhow::Result<()> {
|
||||||
pub async fn prepare_plan(&self) -> anyhow::Result<Cuddle<PreparePlan>> {
|
match self
|
||||||
let plan = if let Some(project) = &self.state.project {
|
.cli
|
||||||
Plan::new().clone_from_project(project).await?
|
.get_matches_from(std::env::args())
|
||||||
} else {
|
.subcommand()
|
||||||
None
|
.ok_or(anyhow::anyhow!("failed to find subcommand"))?
|
||||||
};
|
{
|
||||||
|
("do", args) => {
|
||||||
Ok(Cuddle {
|
tracing::debug!("executing do");
|
||||||
state: PreparePlan {
|
}
|
||||||
project: self.state.project.clone(),
|
_ => {}
|
||||||
plan,
|
}
|
||||||
},
|
|
||||||
})
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
async fn add_default(mut self) -> anyhow::Result<Self> {
|
||||||
impl Cuddle<PreparePlan> {
|
self.cli = self
|
||||||
pub async fn build_state(&self) -> anyhow::Result<Cuddle<ValidatedState>> {
|
.cli
|
||||||
let state = if let Some(project) = &self.state.project {
|
.subcommand(clap::Command::new("do").alias("x"))
|
||||||
let state = state::State::new();
|
.subcommand(clap::Command::new("get"));
|
||||||
let raw_state = state.build_state(project, &self.state.plan).await?;
|
|
||||||
|
Ok(self)
|
||||||
state.validate_state(&raw_state).await?
|
}
|
||||||
} else {
|
|
||||||
ValidatedState::default()
|
async fn add_project_commands(mut self) -> anyhow::Result<Self> {
|
||||||
};
|
if let Some(project) = self.cuddle.state.project.as_ref() {
|
||||||
|
// Add project level commands
|
||||||
Ok(Cuddle { state })
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_plan_commands(mut self) -> anyhow::Result<Self> {
|
||||||
|
if let Some(plan) = self.cuddle.state.plan.as_ref() {
|
||||||
|
// Add plan level commands
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ use crate::{
|
|||||||
schema_validator::SchemaValidator,
|
schema_validator::SchemaValidator,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod validated_project;
|
||||||
|
|
||||||
pub struct State {}
|
pub struct State {}
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -51,87 +53,8 @@ pub struct RawState {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ValidatedState {
|
pub struct ValidatedState {
|
||||||
project: Option<Project>,
|
pub project: Option<Project>,
|
||||||
plan: Option<Plan>,
|
pub plan: Option<Plan>,
|
||||||
}
|
|
||||||
|
|
||||||
mod validated_project {
|
|
||||||
use std::{
|
|
||||||
collections::BTreeMap,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use toml::Table;
|
|
||||||
|
|
||||||
use crate::project::CUDDLE_PROJECT_FILE;
|
|
||||||
|
|
||||||
pub struct Project {
|
|
||||||
value: Value,
|
|
||||||
pub root: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Project {
|
|
||||||
pub fn new(value: Value, root: &Path) -> Self {
|
|
||||||
Self {
|
|
||||||
value,
|
|
||||||
root: root.to_path_buf(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_file(content: &str, root: &Path) -> anyhow::Result<Self> {
|
|
||||||
let table: Table = toml::from_str(content)?;
|
|
||||||
let config = Config::default();
|
|
||||||
|
|
||||||
let project = table
|
|
||||||
.get("project")
|
|
||||||
.ok_or(anyhow!("cuddle.toml doesn't provide a [project] table"))?;
|
|
||||||
|
|
||||||
let value: Value = project.into();
|
|
||||||
|
|
||||||
Ok(Self::new(value, root))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn from_path(path: &Path) -> anyhow::Result<Self> {
|
|
||||||
let cuddle_file = path.join(CUDDLE_PROJECT_FILE);
|
|
||||||
|
|
||||||
if !cuddle_file.exists() {
|
|
||||||
anyhow::bail!("no cuddle.toml project file found");
|
|
||||||
}
|
|
||||||
|
|
||||||
let cuddle_project_file = tokio::fs::read_to_string(cuddle_file).await?;
|
|
||||||
|
|
||||||
Self::from_file(&cuddle_project_file, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct Config {
|
|
||||||
project: BTreeMap<String, Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Value {
|
|
||||||
String(String),
|
|
||||||
Bool(bool),
|
|
||||||
Array(Vec<Value>),
|
|
||||||
Map(BTreeMap<String, Value>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&toml::Value> for Value {
|
|
||||||
fn from(value: &toml::Value) -> Self {
|
|
||||||
match value {
|
|
||||||
toml::Value::String(s) => Self::String(s.clone()),
|
|
||||||
toml::Value::Integer(i) => Self::String(i.to_string()),
|
|
||||||
toml::Value::Float(f) => Self::String(f.to_string()),
|
|
||||||
toml::Value::Boolean(b) => Self::Bool(*b),
|
|
||||||
toml::Value::Datetime(dt) => Self::String(dt.to_string()),
|
|
||||||
toml::Value::Array(array) => Self::Array(array.iter().map(|i| i.into()).collect()),
|
|
||||||
toml::Value::Table(tbl) => {
|
|
||||||
Self::Map(tbl.iter().map(|(k, v)| (k.clone(), v.into())).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Plan {}
|
pub struct Plan {}
|
||||||
|
76
crates/cuddle/src/state/validated_project.rs
Normal file
76
crates/cuddle/src/state/validated_project.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use toml::Table;
|
||||||
|
|
||||||
|
use crate::project::CUDDLE_PROJECT_FILE;
|
||||||
|
|
||||||
|
pub struct Project {
|
||||||
|
value: Value,
|
||||||
|
pub root: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Project {
|
||||||
|
pub fn new(value: Value, root: &Path) -> Self {
|
||||||
|
Self {
|
||||||
|
value,
|
||||||
|
root: root.to_path_buf(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file(content: &str, root: &Path) -> anyhow::Result<Self> {
|
||||||
|
let table: Table = toml::from_str(content)?;
|
||||||
|
let config = Config::default();
|
||||||
|
|
||||||
|
let project = table
|
||||||
|
.get("project")
|
||||||
|
.ok_or(anyhow!("cuddle.toml doesn't provide a [project] table"))?;
|
||||||
|
|
||||||
|
let value: Value = project.into();
|
||||||
|
|
||||||
|
Ok(Self::new(value, root))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn from_path(path: &Path) -> anyhow::Result<Self> {
|
||||||
|
let cuddle_file = path.join(CUDDLE_PROJECT_FILE);
|
||||||
|
|
||||||
|
if !cuddle_file.exists() {
|
||||||
|
anyhow::bail!("no cuddle.toml project file found");
|
||||||
|
}
|
||||||
|
|
||||||
|
let cuddle_project_file = tokio::fs::read_to_string(cuddle_file).await?;
|
||||||
|
|
||||||
|
Self::from_file(&cuddle_project_file, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Config {
|
||||||
|
project: BTreeMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Value {
|
||||||
|
String(String),
|
||||||
|
Bool(bool),
|
||||||
|
Array(Vec<Value>),
|
||||||
|
Map(BTreeMap<String, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&toml::Value> for Value {
|
||||||
|
fn from(value: &toml::Value) -> Self {
|
||||||
|
match value {
|
||||||
|
toml::Value::String(s) => Self::String(s.clone()),
|
||||||
|
toml::Value::Integer(i) => Self::String(i.to_string()),
|
||||||
|
toml::Value::Float(f) => Self::String(f.to_string()),
|
||||||
|
toml::Value::Boolean(b) => Self::Bool(*b),
|
||||||
|
toml::Value::Datetime(dt) => Self::String(dt.to_string()),
|
||||||
|
toml::Value::Array(array) => Self::Array(array.iter().map(|i| i.into()).collect()),
|
||||||
|
toml::Value::Table(tbl) => {
|
||||||
|
Self::Map(tbl.iter().map(|(k, v)| (k.clone(), v.into())).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user