git_graph/
config.rs

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