git_graph/
config.rs

1//! Branching model configurations.
2//!
3//! In this module you will find functions to read and write branching model
4//! configurations on disk.
5//!
6//! The [branching models][BranchSettingsDef] themselves are defined in
7//! module [settings][super::settings]
8
9use crate::settings::{BranchSettingsDef, RepoSettings};
10use git2::Repository;
11use std::ffi::OsStr;
12use std::path::{Path, PathBuf};
13
14/// Creates the directory `APP_DATA/git-graph/models` if it does not exist,
15/// and writes the files for built-in branching models there.
16pub fn create_config<P: AsRef<Path> + AsRef<OsStr>>(app_model_path: &P) -> Result<(), String> {
17    let path: &Path = app_model_path.as_ref();
18    if !path.exists() {
19        std::fs::create_dir_all(app_model_path).map_err(|err| err.to_string())?;
20
21        let models = [
22            (BranchSettingsDef::git_flow(), "git-flow.toml"),
23            (BranchSettingsDef::simple(), "simple.toml"),
24            (BranchSettingsDef::none(), "none.toml"),
25        ];
26        for (model, file) in &models {
27            let mut path = PathBuf::from(&app_model_path);
28            path.push(file);
29            let str = toml::to_string_pretty(&model).map_err(|err| err.to_string())?;
30            std::fs::write(&path, str).map_err(|err| err.to_string())?;
31        }
32    }
33
34    Ok(())
35}
36
37/// Get models available in `APP_DATA/git-graph/models`.
38pub fn get_available_models<P: AsRef<Path>>(app_model_path: &P) -> Result<Vec<String>, String> {
39    let models = std::fs::read_dir(app_model_path)
40        .map_err(|err| err.to_string())?
41        .filter_map(|e| match e {
42            Ok(e) => {
43                if let (Some(name), Some(ext)) = (e.path().file_name(), e.path().extension()) {
44                    if ext == "toml" {
45                        name.to_str()
46                            .map(|name| (name[..(name.len() - 5)]).to_string())
47                    } else {
48                        None
49                    }
50                } else {
51                    None
52                }
53            }
54            Err(_) => None,
55        })
56        .collect::<Vec<_>>();
57
58    Ok(models)
59}
60
61/// Get the currently set branching model for a repo.
62pub fn get_model_name(repository: &Repository, file_name: &str) -> Result<Option<String>, String> {
63    let mut config_path = PathBuf::from(repository.path());
64    config_path.push(file_name);
65
66    if config_path.exists() {
67        let repo_config: RepoSettings =
68            toml::from_str(&std::fs::read_to_string(config_path).map_err(|err| err.to_string())?)
69                .map_err(|err| err.to_string())?;
70
71        Ok(Some(repo_config.model))
72    } else {
73        Ok(None)
74    }
75}
76
77/// Try to get the branch settings for a given model.
78/// If no model name is given, returns the branch settings set for the repo, or the default otherwise.
79pub fn get_model<P: AsRef<Path> + AsRef<OsStr>>(
80    repository: &Repository,
81    model: Option<&str>,
82    repo_config_file: &str,
83    app_model_path: &P,
84) -> Result<BranchSettingsDef, String> {
85    match model {
86        Some(model) => read_model(model, app_model_path),
87        None => {
88            let mut config_path = PathBuf::from(repository.path());
89            config_path.push(repo_config_file);
90
91            if config_path.exists() {
92                let repo_config: RepoSettings = toml::from_str(
93                    &std::fs::read_to_string(config_path).map_err(|err| err.to_string())?,
94                )
95                .map_err(|err| err.to_string())?;
96
97                read_model(&repo_config.model, app_model_path)
98            } else {
99                Ok(read_model("git-flow", app_model_path)
100                    .unwrap_or_else(|_| BranchSettingsDef::git_flow()))
101            }
102        }
103    }
104}
105
106/// Read a branching model file.
107fn read_model<P: AsRef<Path> + AsRef<OsStr>>(
108    model: &str,
109    app_model_path: &P,
110) -> Result<BranchSettingsDef, String> {
111    let mut model_file = PathBuf::from(&app_model_path);
112    model_file.push(format!("{}.toml", model));
113
114    if model_file.exists() {
115        toml::from_str::<BranchSettingsDef>(
116            &std::fs::read_to_string(model_file).map_err(|err| err.to_string())?,
117        )
118        .map_err(|err| err.to_string())
119    } else {
120        let models = get_available_models(&app_model_path)?;
121        let path: &Path = app_model_path.as_ref();
122        Err(format!(
123            "ERROR: No branching model named '{}' found in {}\n       Available models are: {}",
124            model,
125            path.display(),
126            itertools::join(models, ", ")
127        ))
128    }
129}
130/// Permanently sets the branching model for a repository
131pub fn set_model<P: AsRef<Path>>(
132    repository: &Repository,
133    model: &str,
134    repo_config_file: &str,
135    app_model_path: &P,
136) -> Result<(), String> {
137    let models = get_available_models(&app_model_path)?;
138
139    if !models.contains(&model.to_string()) {
140        return Err(format!(
141            "ERROR: No branching model named '{}' found in {}\n       Available models are: {}",
142            model,
143            app_model_path.as_ref().display(),
144            itertools::join(models, ", ")
145        ));
146    }
147
148    let mut config_path = PathBuf::from(repository.path());
149    config_path.push(repo_config_file);
150
151    let config = RepoSettings {
152        model: model.to_string(),
153    };
154
155    let str = toml::to_string_pretty(&config).map_err(|err| err.to_string())?;
156    std::fs::write(&config_path, str).map_err(|err| err.to_string())?;
157
158    Ok(())
159}