feat: can recursively load files

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-05-21 21:46:22 +02:00
parent 06ad4515c2
commit 461f8acb73
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
16 changed files with 83 additions and 18 deletions

View File

@ -1,3 +1,5 @@
#![feature(map_try_insert)]
pub mod components; pub mod components;
pub use components::*; pub use components::*;

View File

@ -5,6 +5,7 @@ use std::{
}; };
use anyhow::Context; use anyhow::Context;
use futures::{future::BoxFuture, FutureExt};
use minijinja::context; use minijinja::context;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio_stream::{wrappers::ReadDirStream, StreamExt}; use tokio_stream::{wrappers::ReadDirStream, StreamExt};
@ -32,6 +33,9 @@ impl Default for ProcessOpts {
} }
} }
const TEMPLATES_PATH_PREFIX: &str = "templates/clusters";
const CUDDLE_PLAN_PATH_PREFIX: &str = ".cuddle/plan";
pub async fn process_opts( pub async fn process_opts(
components: impl IntoIterator<Item = impl IntoComponent>, components: impl IntoIterator<Item = impl IntoComponent>,
opts: ProcessOpts, opts: ProcessOpts,
@ -44,18 +48,17 @@ pub async fn process_opts(
let path = opts.path.canonicalize().context("failed to find folder")?; let path = opts.path.canonicalize().context("failed to find folder")?;
let cuddle_path = path.join("cuddle.yaml"); let cuddle_path = path.join("cuddle.yaml");
let template = path.join("templates").join("clusters");
tracing::debug!( tracing::debug!(
"searching for templates in: {} with cuddle: {}", "searching for templates in: {} with cuddle: {}",
template.display(), path.display(),
cuddle_path.display() cuddle_path.display()
); );
let clusters = read_cuddle_section(&cuddle_path).await?; let clusters = read_cuddle_section(&cuddle_path).await?;
tracing::debug!("found clusters: {:?}", clusters); tracing::debug!("found clusters: {:?}", clusters);
let template_files = load_template_files(&template).await?; let template_files = load_template_files(&path).await?;
tracing::debug!("found files: {:?}", template_files); tracing::debug!("found files: {:?}", template_files);
tokio::fs::remove_dir_all(&opts.output).await?; tokio::fs::remove_dir_all(&opts.output).await?;
@ -100,31 +103,70 @@ async fn read_cuddle_section(path: &Path) -> anyhow::Result<CuddleClusters> {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct TemplateFiles { struct TemplateFiles {
templates: Vec<PathBuf>, templates: HashMap<String, PathBuf>,
raw: Vec<PathBuf>, raw: HashMap<String, PathBuf>,
} }
async fn load_template_files(path: &Path) -> anyhow::Result<TemplateFiles> { impl TemplateFiles {
Ok(TemplateFiles { fn merge(&mut self, template_files: TemplateFiles) {
templates: read_dir(path) for (name, path) in template_files.templates {
// Ignored if the key is already present
let _ = self.templates.try_insert(name, path);
}
for (name, path) in template_files.raw {
// Ignored if the key is already present
let _ = self.raw.try_insert(name, path);
}
}
}
fn load_template_files(path: &Path) -> BoxFuture<'static, anyhow::Result<TemplateFiles>> {
let template_path = path.join(TEMPLATES_PATH_PREFIX);
let path = path.to_path_buf();
tracing::info!("reading folder: {}", path.display());
async move {
let templates = read_dir(&template_path)
.await? .await?
.into_iter() .into_iter()
.filter(|i| i.extension().and_then(|e| e.to_str()) == Some("jinja2")) .filter(|(_, i)| i.extension().and_then(|e| e.to_str()) == Some("jinja2"))
.collect(), .collect();
raw: read_dir(&path.join("raw")).await.unwrap_or_default(), let raw = read_dir(&template_path.join("raw"))
}) .await
.unwrap_or_default();
let mut template_files = TemplateFiles { templates, raw };
let nested_path = path.join(CUDDLE_PLAN_PATH_PREFIX);
tracing::info!("trying to read: {}", nested_path.display());
if nested_path.exists() {
let nested = load_template_files(&nested_path).await?;
template_files.merge(nested);
}
Ok(template_files)
}
.boxed()
} }
async fn read_dir(path: &Path) -> anyhow::Result<Vec<PathBuf>> { async fn read_dir(path: &Path) -> anyhow::Result<HashMap<String, PathBuf>> {
let template_dir = tokio::fs::read_dir(path).await?; let template_dir = tokio::fs::read_dir(path).await?;
let mut template_dir_stream = ReadDirStream::new(template_dir); let mut template_dir_stream = ReadDirStream::new(template_dir);
let mut paths = Vec::new(); let mut paths = HashMap::new();
while let Some(entry) = template_dir_stream.next().await { while let Some(entry) = template_dir_stream.next().await {
let entry = entry?; let entry = entry?;
if entry.metadata().await?.is_file() { if entry.metadata().await?.is_file() {
paths.push(entry.path()); paths.insert(
entry
.path()
.file_name()
.expect("the file to have a filename")
.to_string_lossy()
.to_string(),
entry.path(),
);
} }
} }
@ -139,7 +181,7 @@ async fn process_templates(
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
for (environment, value) in clusters.iter() { for (environment, value) in clusters.iter() {
process_cluster( process_cluster(
&components, components,
value, value,
environment, environment,
template_files, template_files,
@ -158,11 +200,11 @@ async fn process_cluster(
template_files: &TemplateFiles, template_files: &TemplateFiles,
dest: &Path, dest: &Path,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
for template_file in &template_files.templates { for (_, template_file) in &template_files.templates {
process_template_file(components, value, environment, template_file, dest).await?; process_template_file(components, value, environment, template_file, dest).await?;
} }
for raw_file in &template_files.raw { for (_, raw_file) in &template_files.raw {
process_raw_file(environment, raw_file, dest).await?; process_raw_file(environment, raw_file, dest).await?;
} }

View File

@ -0,0 +1,2 @@
cuddle/clusters:
dev:

View File

@ -0,0 +1 @@
nested

View File

@ -0,0 +1 @@
nested

View File

@ -0,0 +1 @@
service

View File

@ -0,0 +1 @@
service

View File

@ -0,0 +1 @@
service

View File

@ -0,0 +1 @@
service

View File

@ -0,0 +1 @@
service

View File

@ -0,0 +1 @@
service

View File

@ -0,0 +1 @@
service

View File

@ -27,6 +27,13 @@ async fn both() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[tokio::test]
async fn nested() -> anyhow::Result<()> {
run_test("nested").await?;
Ok(())
}
#[tokio::test] #[tokio::test]
async fn jinja() -> anyhow::Result<()> { async fn jinja() -> anyhow::Result<()> {
run_test("jinja").await?; run_test("jinja").await?;