mod generic;
mod zola;
use anyhow::Result;
use parking_lot::Mutex;
use serde::Deserialize;
use std::path::PathBuf;
use crate::graph::{BuildGraph, Product};
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct PlannedOutput {
pub path: PathBuf,
pub sources: Vec<PathBuf>,
}
enum ToolRun {
NotRun,
Succeeded,
Failed(String),
}
pub struct OnceTool {
state: Mutex<ToolRun>,
}
impl OnceTool {
pub const fn new() -> Self {
Self {
state: Mutex::new(ToolRun::NotRun),
}
}
pub fn run(&self, tool: &str, run: impl FnOnce() -> Result<()>) -> Result<()> {
let mut state = self.state.lock();
match &*state {
ToolRun::Succeeded => return Ok(()),
ToolRun::Failed(msg) => {
anyhow::bail!("{tool} already failed earlier in this build: {msg}")
}
ToolRun::NotRun => {}
}
let result = run();
*state = match &result {
Ok(()) => ToolRun::Succeeded,
Err(e) => ToolRun::Failed(format!("{e:#}")),
};
result
}
}
pub fn add_planned_products(
graph: &mut BuildGraph,
plan: &[PlannedOutput],
instance_name: &str,
config_hash: &str,
extra: &[PathBuf],
) -> Result<()> {
for planned in plan {
let mut inputs = planned.sources.clone();
for input in extra {
if !inputs.contains(input) {
inputs.push(input.clone());
}
}
let hash = crate::checksum::hash_parts(&[config_hash, &planned.path.display().to_string()]);
graph.add_product(
inputs,
vec![planned.path.clone()],
instance_name,
Some(hash),
)?;
}
Ok(())
}
pub fn require_planned_output(product: &Product, tool: &str) -> Result<()> {
let output = product.primary_output();
if !output.is_file() {
anyhow::bail!(
"{tool} did not produce the planned output {}",
output.display()
);
}
Ok(())
}