206 lines
5.5 KiB
Rust
206 lines
5.5 KiB
Rust
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,
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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"));
|
|
}
|
|
|
|
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"
|
|
))
|
|
}
|