Added cli for subcommands
This commit is contained in:
@@ -11,3 +11,4 @@ serde = { version = "1.0.143", features = ["derive"] }
|
||||
serde_yaml = "0.9.4"
|
||||
walkdir = "2.3.2"
|
||||
git2 = { version = "0.15.0", features = ["ssh"] }
|
||||
clap = "3.2.16"
|
||||
|
97
cuddle_cli/src/cli.rs
Normal file
97
cuddle_cli/src/cli.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use clap::Command;
|
||||
|
||||
use crate::{context::CuddleContext, model::CuddleScript};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct CuddleAction {
|
||||
script: CuddleScript,
|
||||
path: PathBuf,
|
||||
name: String,
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
impl CuddleAction {
|
||||
pub fn new(script: CuddleScript, path: PathBuf, name: String) -> Self {
|
||||
Self { script, path, name }
|
||||
}
|
||||
|
||||
pub fn execute(self) {
|
||||
match self.script {
|
||||
CuddleScript::Shell(s) => {}
|
||||
CuddleScript::Dagger(d) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CuddleCli<'a> {
|
||||
scripts: Vec<CuddleAction>,
|
||||
context: Arc<Mutex<Vec<CuddleContext>>>,
|
||||
command: Option<Command<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> CuddleCli<'a> {
|
||||
pub fn new(context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<CuddleCli<'a>> {
|
||||
let mut cli = CuddleCli {
|
||||
scripts: vec![],
|
||||
context: context.clone(),
|
||||
command: None,
|
||||
};
|
||||
|
||||
cli = cli.process_scripts().build_cli();
|
||||
|
||||
Ok(cli)
|
||||
}
|
||||
|
||||
fn process_scripts(mut self) -> Self {
|
||||
if let Ok(context_iter) = self.context.clone().lock() {
|
||||
for ctx in context_iter.iter() {
|
||||
if let Some(scripts) = ctx.plan.scripts.clone() {
|
||||
for (name, script) in scripts {
|
||||
self.scripts
|
||||
.push(CuddleAction::new(script.clone(), ctx.path.clone(), name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn build_cli(mut self) -> Self {
|
||||
let mut root_cmd = Command::new("cuddle")
|
||||
.version("1.0")
|
||||
.author("kjuulh <contact@kasperhermansen.com>")
|
||||
.about("cuddle is your domain specific organization tool. It enabled widespread sharing through repositories, as well as collaborating while maintaining speed and integrity")
|
||||
.propagate_version(true)
|
||||
.arg_required_else_help(true);
|
||||
|
||||
let mut execute_cmd = Command::new("x").about("x is your entry into your domains scripts, scripts inherited from parents will also be present here");
|
||||
|
||||
for script in self.scripts.iter() {
|
||||
let action_cmd = Command::new(script.name.clone());
|
||||
|
||||
// TODO: Some way to add an about for clap, requires conversion from String -> &str
|
||||
execute_cmd = execute_cmd.subcommand(action_cmd);
|
||||
}
|
||||
|
||||
root_cmd = root_cmd.subcommand(execute_cmd);
|
||||
|
||||
self.command = Some(root_cmd);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn execute(self) -> Self {
|
||||
if let Some(cli) = self.command.clone() {
|
||||
let _ = cli.get_matches();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
179
cuddle_cli/src/context.rs
Normal file
179
cuddle_cli/src/context.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
use std::{
|
||||
env::{self, current_dir},
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use git2::{build::RepoBuilder, FetchOptions, RemoteCallbacks};
|
||||
|
||||
use crate::model::{CuddleBase, CuddlePlan};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CuddleContext {
|
||||
pub plan: CuddlePlan,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
pub fn extract_cuddle() -> anyhow::Result<Arc<Mutex<Vec<CuddleContext>>>> {
|
||||
let mut curr_dir = current_dir()?;
|
||||
curr_dir.push(".cuddle/");
|
||||
if let Err(res) = std::fs::remove_dir_all(curr_dir) {
|
||||
println!("{}", res);
|
||||
}
|
||||
|
||||
// Load main cuddle file
|
||||
let cuddle_yaml = find_root_cuddle()?;
|
||||
// TODO: Set trace
|
||||
println!("{}", cuddle_yaml);
|
||||
let cuddle_plan = serde_yaml::from_str::<CuddlePlan>(cuddle_yaml.as_str())?;
|
||||
|
||||
// TODO: Set debug
|
||||
println!("{:?}", cuddle_plan);
|
||||
|
||||
let context: Arc<Mutex<Vec<CuddleContext>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
context.lock().unwrap().push(CuddleContext {
|
||||
plan: cuddle_plan.clone(),
|
||||
path: current_dir()?,
|
||||
});
|
||||
|
||||
// pull parent plan and execute recursive descent
|
||||
match cuddle_plan.base {
|
||||
CuddleBase::Bool(true) => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"plan cannot be enabled without specifying a plan"
|
||||
))
|
||||
}
|
||||
CuddleBase::Bool(false) => {
|
||||
println!("plan is root skipping")
|
||||
}
|
||||
CuddleBase::String(parent_plan) => {
|
||||
let destination_path = create_cuddle_local()?;
|
||||
let mut cuddle_dest = destination_path.clone();
|
||||
cuddle_dest.push("base");
|
||||
|
||||
pull_parent_cuddle_into_local(parent_plan, cuddle_dest.clone())?;
|
||||
recurse_parent(cuddle_dest, context.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(ctx) = context.clone().lock() {
|
||||
// TODO: set trace
|
||||
println!("{:?}", ctx)
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("could not acquire lock"));
|
||||
}
|
||||
|
||||
Ok(context)
|
||||
}
|
||||
|
||||
fn create_cuddle_local() -> anyhow::Result<PathBuf> {
|
||||
let mut curr_dir = current_dir()?;
|
||||
curr_dir.push(".cuddle/");
|
||||
|
||||
if curr_dir.exists() {
|
||||
println!(".cuddle already exists skipping");
|
||||
return Ok(curr_dir);
|
||||
}
|
||||
|
||||
std::fs::create_dir(curr_dir.clone())?;
|
||||
|
||||
Ok(curr_dir)
|
||||
}
|
||||
|
||||
fn create_cuddle(path: PathBuf) -> anyhow::Result<PathBuf> {
|
||||
let mut curr_dir = path.clone();
|
||||
curr_dir.push(".cuddle/");
|
||||
|
||||
if curr_dir.exists() {
|
||||
println!(".cuddle already exists skipping");
|
||||
return Ok(curr_dir);
|
||||
}
|
||||
|
||||
std::fs::create_dir(curr_dir.clone())?;
|
||||
|
||||
Ok(curr_dir)
|
||||
}
|
||||
|
||||
fn pull_parent_cuddle_into_local(
|
||||
parent_cuddle: String,
|
||||
destination: PathBuf,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut rc = RemoteCallbacks::new();
|
||||
rc.credentials(|_url, username_from_url, _allowed_types| {
|
||||
git2::Cred::ssh_key(
|
||||
username_from_url.unwrap(),
|
||||
None,
|
||||
Path::new(&format!("{}/.ssh/id_ed25519", env::var("HOME").unwrap())),
|
||||
None,
|
||||
)
|
||||
});
|
||||
|
||||
let mut fo = FetchOptions::new();
|
||||
fo.remote_callbacks(rc);
|
||||
|
||||
RepoBuilder::new()
|
||||
.fetch_options(fo)
|
||||
.clone(&parent_cuddle, &destination)?;
|
||||
|
||||
println!("pulled: {}", parent_cuddle);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recurse_parent(path: PathBuf, context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<()> {
|
||||
let cuddle_contents = find_cuddle(path.clone())?;
|
||||
let cuddle_plan = serde_yaml::from_str::<CuddlePlan>(&cuddle_contents)?;
|
||||
|
||||
let ctx = context.clone();
|
||||
if let Ok(mut ctxs) = ctx.lock() {
|
||||
ctxs.push(CuddleContext {
|
||||
plan: cuddle_plan.clone(),
|
||||
path: path.clone(),
|
||||
});
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Could not acquire lock, aborting"));
|
||||
}
|
||||
|
||||
match cuddle_plan.base {
|
||||
CuddleBase::Bool(true) => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"plan cannot be enabled without specifying a plan"
|
||||
))
|
||||
}
|
||||
CuddleBase::Bool(false) => {
|
||||
println!("plan is root, finishing up");
|
||||
return Ok(());
|
||||
}
|
||||
CuddleBase::String(parent_plan) => {
|
||||
let destination_path = create_cuddle(path.clone())?;
|
||||
let mut cuddle_dest = destination_path.clone();
|
||||
cuddle_dest.push("base");
|
||||
|
||||
pull_parent_cuddle_into_local(parent_plan, cuddle_dest.clone())?;
|
||||
return recurse_parent(cuddle_dest, context.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_root_cuddle() -> anyhow::Result<String> {
|
||||
// TODO: Make recursive towards root
|
||||
let current_dir = env::current_dir()?;
|
||||
find_cuddle(current_dir)
|
||||
}
|
||||
|
||||
fn find_cuddle(path: PathBuf) -> anyhow::Result<String> {
|
||||
for entry in std::fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
let metadata = std::fs::metadata(&path)?;
|
||||
if metadata.is_file() && path.file_name().unwrap() == OsStr::new("cuddle.yaml") {
|
||||
return Ok(std::fs::read_to_string(path)?);
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"Could not find 'cuddle.yaml' in the current directory"
|
||||
))
|
||||
}
|
@@ -1,205 +1,13 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::{self, current_dir},
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use git2::{build::RepoBuilder, FetchOptions, RemoteCallbacks};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum CuddleBase {
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
struct CuddleShellScript {}
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
struct CuddleDaggerScript {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
enum CuddleScript {
|
||||
#[serde(alias = "shell")]
|
||||
Shell(CuddleShellScript),
|
||||
#[serde(alias = "dagger")]
|
||||
Dagger(CuddleDaggerScript),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
struct CuddlePlan {
|
||||
pub base: CuddleBase,
|
||||
pub scripts: Option<HashMap<String, CuddleScript>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CuddleContext {
|
||||
pub plan: CuddlePlan,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
mod cli;
|
||||
mod context;
|
||||
mod model;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let mut curr_dir = current_dir()?;
|
||||
curr_dir.push(".cuddle/");
|
||||
if let Err(res) = std::fs::remove_dir_all(curr_dir) {
|
||||
println!("{}", res);
|
||||
}
|
||||
let context = context::extract_cuddle()?;
|
||||
let mut cuddle_cli = cli::CuddleCli::new(context.clone())?;
|
||||
cuddle_cli = cuddle_cli.execute();
|
||||
|
||||
// Load main cuddle file
|
||||
let cuddle_yaml = find_root_cuddle()?;
|
||||
// TODO: Set trace
|
||||
println!("{}", cuddle_yaml);
|
||||
let cuddle_plan = serde_yaml::from_str::<CuddlePlan>(cuddle_yaml.as_str())?;
|
||||
|
||||
// TODO: Set debug
|
||||
println!("{:?}", cuddle_plan);
|
||||
|
||||
let context: Arc<Mutex<Vec<CuddleContext>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
context.lock().unwrap().push(CuddleContext {
|
||||
plan: cuddle_plan.clone(),
|
||||
path: current_dir()?,
|
||||
});
|
||||
|
||||
// pull parent plan and execute recursive descent
|
||||
match cuddle_plan.base {
|
||||
CuddleBase::Bool(true) => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"plan cannot be enabled without specifying a plan"
|
||||
))
|
||||
}
|
||||
CuddleBase::Bool(false) => {
|
||||
println!("plan is root skipping")
|
||||
}
|
||||
CuddleBase::String(parent_plan) => {
|
||||
let destination_path = create_cuddle_local()?;
|
||||
let mut cuddle_dest = destination_path.clone();
|
||||
cuddle_dest.push("base");
|
||||
|
||||
pull_parent_cuddle_into_local(parent_plan, cuddle_dest.clone())?;
|
||||
recurse_parent(cuddle_dest, context.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(ctx) = context.lock() {
|
||||
println!("{:?}", ctx)
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("could not acquire lock"));
|
||||
}
|
||||
println!("{:?}", cuddle_cli);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_cuddle_local() -> anyhow::Result<PathBuf> {
|
||||
let mut curr_dir = current_dir()?;
|
||||
curr_dir.push(".cuddle/");
|
||||
|
||||
if curr_dir.exists() {
|
||||
println!(".cuddle already exists skipping");
|
||||
return Ok(curr_dir);
|
||||
}
|
||||
|
||||
std::fs::create_dir(curr_dir.clone())?;
|
||||
|
||||
Ok(curr_dir)
|
||||
}
|
||||
|
||||
fn create_cuddle(path: PathBuf) -> anyhow::Result<PathBuf> {
|
||||
let mut curr_dir = path.clone();
|
||||
curr_dir.push(".cuddle/");
|
||||
|
||||
if curr_dir.exists() {
|
||||
println!(".cuddle already exists skipping");
|
||||
return Ok(curr_dir);
|
||||
}
|
||||
|
||||
std::fs::create_dir(curr_dir.clone())?;
|
||||
|
||||
Ok(curr_dir)
|
||||
}
|
||||
|
||||
fn pull_parent_cuddle_into_local(
|
||||
parent_cuddle: String,
|
||||
destination: PathBuf,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut rc = RemoteCallbacks::new();
|
||||
rc.credentials(|_url, username_from_url, _allowed_types| {
|
||||
git2::Cred::ssh_key(
|
||||
username_from_url.unwrap(),
|
||||
None,
|
||||
Path::new(&format!("{}/.ssh/id_ed25519", env::var("HOME").unwrap())),
|
||||
None,
|
||||
)
|
||||
});
|
||||
|
||||
let mut fo = FetchOptions::new();
|
||||
fo.remote_callbacks(rc);
|
||||
|
||||
RepoBuilder::new()
|
||||
.fetch_options(fo)
|
||||
.clone(&parent_cuddle, &destination)?;
|
||||
|
||||
println!("pulled: {}", parent_cuddle);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recurse_parent(path: PathBuf, context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<()> {
|
||||
let cuddle_contents = find_cuddle(path.clone())?;
|
||||
let cuddle_plan = serde_yaml::from_str::<CuddlePlan>(&cuddle_contents)?;
|
||||
|
||||
let ctx = context.clone();
|
||||
if let Ok(mut ctxs) = ctx.lock() {
|
||||
ctxs.push(CuddleContext {
|
||||
plan: cuddle_plan.clone(),
|
||||
path: path.clone(),
|
||||
});
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Could not acquire lock, aborting"));
|
||||
}
|
||||
|
||||
match cuddle_plan.base {
|
||||
CuddleBase::Bool(true) => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"plan cannot be enabled without specifying a plan"
|
||||
))
|
||||
}
|
||||
CuddleBase::Bool(false) => {
|
||||
println!("plan is root, finishing up");
|
||||
return Ok(());
|
||||
}
|
||||
CuddleBase::String(parent_plan) => {
|
||||
let destination_path = create_cuddle(path.clone())?;
|
||||
let mut cuddle_dest = destination_path.clone();
|
||||
cuddle_dest.push("base");
|
||||
|
||||
pull_parent_cuddle_into_local(parent_plan, cuddle_dest.clone())?;
|
||||
return recurse_parent(cuddle_dest, context.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_root_cuddle() -> anyhow::Result<String> {
|
||||
// TODO: Make recursive towards root
|
||||
let current_dir = env::current_dir()?;
|
||||
find_cuddle(current_dir)
|
||||
}
|
||||
|
||||
fn find_cuddle(path: PathBuf) -> anyhow::Result<String> {
|
||||
for entry in std::fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
let metadata = std::fs::metadata(&path)?;
|
||||
if metadata.is_file() && path.file_name().unwrap() == OsStr::new("cuddle.yaml") {
|
||||
return Ok(std::fs::read_to_string(path)?);
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"Could not find 'cuddle.yaml' in the current directory"
|
||||
))
|
||||
}
|
||||
|
34
cuddle_cli/src/model.rs
Normal file
34
cuddle_cli/src/model.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CuddleBase {
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
pub struct CuddleShellScript {
|
||||
pub description: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
pub struct CuddleDaggerScript {
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum CuddleScript {
|
||||
#[serde(alias = "shell")]
|
||||
Shell(CuddleShellScript),
|
||||
#[serde(alias = "dagger")]
|
||||
Dagger(CuddleDaggerScript),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
pub struct CuddlePlan {
|
||||
pub base: CuddleBase,
|
||||
pub scripts: Option<HashMap<String, CuddleScript>>,
|
||||
}
|
Reference in New Issue
Block a user