1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/// ref. <https://pre-commit.com/#pre-commit-configyaml---top-level>
use std::collections::BTreeMap;
use std::fs::File;

use crate::{Hook, Repo, Result, CI};

pub static PRE_COMMIT_CONFIG_PATH: &str = ".pre-commit-config.yaml";

#[serde_with::skip_serializing_none]
#[derive(serde::Serialize, serde::Deserialize, Debug, Eq, Ord, PartialEq, PartialOrd, Clone)]
pub struct PreCommitConfig {
    ci: Option<CI>,
    repos: Vec<Repo>,
    default_install_hook_types: Option<Vec<String>>,
    default_language_version: Option<BTreeMap<String, String>>,
    default_stages: Option<Vec<String>>,
    files: Option<String>,
    exclude: Option<String>,
    fail_fast: Option<bool>,
    minimum_pre_commit_version: Option<String>,
}

impl PreCommitConfig {
    #[must_use]
    pub const fn new() -> Self {
        Self {
            ci: None,
            repos: Vec::new(),
            default_install_hook_types: None,
            default_language_version: None,
            default_stages: None,
            files: None,
            exclude: None,
            fail_fast: None,
            minimum_pre_commit_version: None,
        }
    }

    pub fn read() -> Result<Self> {
        let input = File::open(PRE_COMMIT_CONFIG_PATH)?;
        Ok(serde_yaml::from_reader(input)?)
    }

    pub fn write(&self) -> Result<()> {
        let output = File::create(PRE_COMMIT_CONFIG_PATH)?;
        Ok(serde_yaml::to_writer(output, &self)?)
    }

    pub fn add_repo(&mut self, repo: Repo) {
        self.repos.push(repo);
    }

    /// Sort and deduplicate repos and their hooks
    pub fn sort(&mut self) {
        for repo in &mut self.repos {
            repo.sort();
        }
        self.repos.sort();
        self.repos.dedup();
    }

    /// Install pre-commit-sort in this .pre-commit-config.yaml
    pub fn install(&mut self) {
        let mut repo = Repo::new(
            env!("CARGO_PKG_REPOSITORY").to_string(),
            env!("CARGO_PKG_VERSION").to_string(),
        );
        let hook = Hook::new(env!("CARGO_PKG_NAME").to_string());
        repo.add_hook(hook);
        self.add_repo(repo);
    }
}

impl Default for PreCommitConfig {
    fn default() -> Self {
        Self::new()
    }
}