feat: implement template plan
This commit is contained in:
parent
ef8deadfd8
commit
9be64b74b2
@ -11,6 +11,107 @@ impl Template {
|
|||||||
pub async fn execute(self, project_path: &Path, context: &Context) -> anyhow::Result<()> {
|
pub async fn execute(self, project_path: &Path, context: &Context) -> anyhow::Result<()> {
|
||||||
tracing::info!("templating");
|
tracing::info!("templating");
|
||||||
|
|
||||||
|
self.execute_plan(project_path, context).await?;
|
||||||
|
self.execute_project(project_path, context).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute_plan(&self, project_path: &Path, context: &Context) -> anyhow::Result<()> {
|
||||||
|
let plan_path = &project_path.join(".forest").join("plan");
|
||||||
|
|
||||||
|
let Some(Some(template)) = &context.plan.as_ref().map(|p| &p.templates) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
match template.ty {
|
||||||
|
TemplateType::Jinja2 => {
|
||||||
|
for entry in glob::glob(&format!(
|
||||||
|
"{}/{}",
|
||||||
|
plan_path.display().to_string().trim_end_matches("/"),
|
||||||
|
template.path.trim_start_matches("./"),
|
||||||
|
))
|
||||||
|
.map_err(|e| anyhow::anyhow!("failed to read glob pattern: {}", e))?
|
||||||
|
{
|
||||||
|
let entry = entry.map_err(|e| anyhow::anyhow!("failed to read path: {}", e))?;
|
||||||
|
let entry_name = entry.display().to_string();
|
||||||
|
|
||||||
|
let entry_rel = if entry.is_absolute() {
|
||||||
|
entry.strip_prefix(plan_path).map(|e| e.to_path_buf())
|
||||||
|
} else {
|
||||||
|
Ok(entry.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let rel_file_path = entry_rel
|
||||||
|
.map(|p| {
|
||||||
|
if p.file_name()
|
||||||
|
.map(|f| f.to_string_lossy().ends_with(".jinja2"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
p.with_file_name(
|
||||||
|
p.file_stem().expect("to be able to find a filename"),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
p.to_path_buf()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"failed to find relative file: {}, project: {}, file: {}",
|
||||||
|
e,
|
||||||
|
plan_path.display(),
|
||||||
|
entry_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let output_file_path = project_path
|
||||||
|
.join(".forest/temp")
|
||||||
|
.join(&template.output)
|
||||||
|
.join(rel_file_path);
|
||||||
|
|
||||||
|
let contents = tokio::fs::read_to_string(&entry).await.map_err(|e| {
|
||||||
|
anyhow::anyhow!("failed to read template: {}, err: {}", entry.display(), e)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut env = minijinja::Environment::new();
|
||||||
|
env.add_template(&entry_name, &contents)?;
|
||||||
|
env.add_global("global", &context.project.global);
|
||||||
|
|
||||||
|
let tmpl = env.get_template(&entry_name)?;
|
||||||
|
|
||||||
|
let output = tmpl
|
||||||
|
.render(minijinja::context! {})
|
||||||
|
.map_err(|e| anyhow::anyhow!("failed to render template: {}", e))?;
|
||||||
|
|
||||||
|
tracing::info!("rendered template: {}", output);
|
||||||
|
|
||||||
|
if let Some(parent) = output_file_path.parent() {
|
||||||
|
tokio::fs::create_dir_all(parent).await.map_err(|e| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"failed to create directory (path: {}) for output: {}",
|
||||||
|
parent.display(),
|
||||||
|
e
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output_file = tokio::fs::File::create(&output_file_path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"failed to create file: {}, error: {}",
|
||||||
|
output_file_path.display(),
|
||||||
|
e
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
output_file.write_all(output.as_bytes()).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn execute_project(&self, project_path: &Path, context: &Context) -> anyhow::Result<()> {
|
||||||
let Some(template) = &context.project.templates else {
|
let Some(template) = &context.project.templates else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,8 @@ pub struct Context {
|
|||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct Plan {
|
pub struct Plan {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub templates: Option<Templates>,
|
||||||
|
pub scripts: Option<Scripts>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<KdlDocument> for Plan {
|
impl TryFrom<KdlDocument> for Plan {
|
||||||
@ -35,6 +37,14 @@ impl TryFrom<KdlDocument> for Plan {
|
|||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or(anyhow::anyhow!("a forest kuddle plan must have a name"))?,
|
.ok_or(anyhow::anyhow!("a forest kuddle plan must have a name"))?,
|
||||||
|
templates: plan_children
|
||||||
|
.get("templates")
|
||||||
|
.map(|t| t.try_into())
|
||||||
|
.transpose()?,
|
||||||
|
scripts: plan_children
|
||||||
|
.get("scripts")
|
||||||
|
.map(|m| m.try_into())
|
||||||
|
.transpose()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
plan {
|
plan {
|
||||||
name project
|
name project
|
||||||
|
|
||||||
|
templates type=jinja2 {
|
||||||
|
path "templates/*.jinja2"
|
||||||
|
output "output/"
|
||||||
|
}
|
||||||
|
|
||||||
|
scripts {
|
||||||
|
world type=shell {}
|
||||||
|
hello type=shell {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
examples/plan/scripts/hello.sh
Executable file
5
examples/plan/scripts/hello.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "hello plan"
|
5
examples/plan/scripts/world.sh
Executable file
5
examples/plan/scripts/world.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "hello plan world"
|
3
examples/plan/templates/something.plan.yaml.jinja2
Normal file
3
examples/plan/templates/something.plan.yaml.jinja2
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
something plan
|
||||||
|
|
||||||
|
val is mapping: {{ global.someKey.some.key.val is mapping }}
|
3
examples/plan/templates/something.yaml.jinja2
Normal file
3
examples/plan/templates/something.yaml.jinja2
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
something plan
|
||||||
|
|
||||||
|
val is mapping: {{ global.someKey.some.key.val is mapping }}
|
Loading…
x
Reference in New Issue
Block a user