xtask_toolkit/precommit/
mod.rs

1use minijinja::{Environment, context};
2
3pub const PRECOMMIT_TEMPLATE: &str = include_str!("precommit.py.j2");
4
5#[derive(Copy, Clone, Debug, serde::Deserialize, serde::Serialize)]
6pub struct Features {
7    pub cargo: bool,
8    pub taplo: bool,
9    pub gitleaks: bool,
10}
11
12impl Default for Features {
13    fn default() -> Self {
14        Self {
15            cargo: true,
16            taplo: true,
17            gitleaks: true,
18        }
19    }
20}
21
22#[derive(Debug, thiserror::Error)]
23pub enum PrecommitError {
24    #[error(transparent)]
25    JinjaError(#[from] minijinja::Error),
26
27    #[error(transparent)]
28    WriteError(#[from] std::io::Error),
29
30    #[error("Project root cannot be found")]
31    ProjectRootError,
32}
33
34impl From<xshell::Error> for PrecommitError {
35    fn from(_: xshell::Error) -> Self {
36        PrecommitError::ProjectRootError
37    }
38}
39
40pub fn install_precommit(features: Features) -> Result<(), PrecommitError> {
41    let mut env = Environment::new();
42    env.add_template("precommit", PRECOMMIT_TEMPLATE)?;
43    let tmpl = env.get_template("precommit")?;
44    let context = context!(
45        features => features,
46    );
47    let rendered = tmpl.render(context)?;
48
49    let root = crate::git::get_root_path()?;
50    let dest = root.join(".git").join("hooks").join("pre-commit");
51
52    std::fs::write(dest, rendered.as_bytes())?;
53
54    Ok(())
55}