use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use gize_core::Manifest;
use crate::plan::Plan;
use crate::writer::{Options, Report, Writer};
#[derive(Debug, Clone)]
pub struct GenContext {
pub manifest: Manifest,
pub root: PathBuf,
}
impl GenContext {
pub fn from_root(root: impl Into<PathBuf>) -> Result<Self> {
let root = root.into();
let manifest_path = root.join("gize.toml");
let text = std::fs::read_to_string(&manifest_path)
.with_context(|| format!("reading {}", manifest_path.display()))?;
let manifest = Manifest::from_toml(&text)?;
Ok(Self { manifest, root })
}
pub fn from_current_dir() -> Result<Self> {
Self::from_root(Path::new("."))
}
}
pub trait Generator {
fn name(&self) -> &str;
fn plan(&self, ctx: &GenContext, args: &[String]) -> Result<Plan>;
}
pub fn run(generator: &dyn Generator, args: &[String], opts: Options) -> Result<Report> {
let ctx = GenContext::from_current_dir().context("not a gize project here (no gize.toml)")?;
let plan = generator
.plan(&ctx, args)
.with_context(|| format!("plugin `{}` failed to build its plan", generator.name()))?;
Writer::new(opts).apply(&ctx.root, &plan)
}
pub fn run_in(
ctx: &GenContext,
generator: &dyn Generator,
args: &[String],
opts: Options,
) -> Result<Report> {
let plan = generator.plan(ctx, args)?;
Writer::new(opts).apply(&ctx.root, &plan)
}
#[cfg(test)]
mod tests {
use super::*;
struct Dummy;
impl Generator for Dummy {
fn name(&self) -> &str {
"dummy"
}
fn plan(&self, _ctx: &GenContext, _args: &[String]) -> Result<Plan> {
Ok(Plan::new().create("generated/by_plugin.txt", "hello from a plugin\n"))
}
}
#[test]
fn a_plugin_generates_through_the_safe_writer() {
use std::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let root = std::env::temp_dir().join(format!(
"gize-plugin-{}-{}",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
));
std::fs::create_dir_all(&root).unwrap();
std::fs::write(root.join("gize.toml"), "[project]\nname = \"demo\"\n").unwrap();
let ctx = GenContext::from_root(&root).unwrap();
let report = run_in(&ctx, &Dummy, &[], Options::default()).unwrap();
assert_eq!(report.created, vec!["generated/by_plugin.txt".to_string()]);
assert!(root.join("generated/by_plugin.txt").is_file());
let again = run_in(&ctx, &Dummy, &[], Options::default()).unwrap();
assert_eq!(again.skipped, vec!["generated/by_plugin.txt".to_string()]);
}
}