feat: refactor projects

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2024-10-26 22:27:16 +02:00
parent 350a3669b0
commit 6729f6e794
20 changed files with 614 additions and 220 deletions

View File

@@ -1,163 +1 @@
use std::{
collections::BTreeMap,
future::Future,
ops::Deref,
path::{Path, PathBuf},
pin::Pin,
sync::Arc,
};
use rust_builder::RustActionsBuilder;
use serde::{Deserialize, Serialize};
use crate::state::validated_project::Value;
pub mod builder;
pub mod rust_builder;
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<ExecutableActions> {
let mut executable_actions = Vec::default();
self.clean_cache().await?;
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!(),
}
}
let mut exec_actions = ExecutableActions::default();
for mut actions in executable_actions {
exec_actions.actions.append(&mut actions.actions);
}
Ok(exec_actions)
}
fn global_registry(&self) -> anyhow::Result<Option<PathBuf>> {
if let Some(dir) = dirs::cache_dir().map(|c| c.join("sh.cuddle/registry/actions/rust")) {
if !dir.exists() {
std::fs::create_dir_all(&dir)?;
}
Ok(Some(dir))
} else {
Ok(None)
}
}
/// clean_cache checks whether a given function has been used in the last month, if it hasn't it is automatically removed, and potentially reconstructed again on demand
pub async fn clean_cache(&mut self) -> anyhow::Result<()> {
let now = std::time::SystemTime::now();
let mut file_stream = tokio::fs::read_dir(
self.global_registry()?
.ok_or(anyhow::anyhow!("failed to get global registry"))?,
)
.await?;
while let Ok(Some(entry)) = file_stream.next_entry().await {
tracing::trace!("checking file: {}", entry.path().display());
let metadata = entry.metadata().await?;
if metadata.is_dir() {
let modified = metadata.modified()?;
let cache_threshold = now
.checked_sub(std::time::Duration::from_secs(60 * 24 * 30)) // Cache duration is a month
.expect("to be able to have a systemtime above a week");
if modified < cache_threshold {
tracing::debug!("cleaning up entry: {}", entry.path().display());
tokio::fs::remove_dir_all(entry.path()).await?;
}
}
}
Ok(())
}
}
pub enum ActionVariant {
Rust { root_path: PathBuf },
Docker,
}
#[derive(Default)]
pub struct ExecutableActions {
pub actions: Vec<ExecutableAction>,
}
pub struct ExecutableAction {
pub name: String,
pub description: String,
pub flags: BTreeMap<String, ExecutableActionFlag>,
call_fn: LazyResolve,
}
impl ExecutableAction {
pub async fn call(&self) -> anyhow::Result<()> {
// Bad hack until .call becomes stable
(self.call_fn.0)().await?;
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ExecutableActionFlag {
String,
Bool,
}
struct LazyResolve(
Arc<dyn Fn() -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> + Send + Sync>,
);
impl LazyResolve {
pub fn new(
func: Box<
dyn Fn() -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> + Send + Sync,
>,
) -> Self {
Self(Arc::new(func))
}
}
impl Deref for LazyResolve {
type Target =
Arc<dyn Fn() -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> + Send + Sync>;
fn deref(&self) -> &Self::Target {
&self.0
}
}