gr/
init.rs

1use std::fs::OpenOptions;
2use std::io::{ErrorKind, Write};
3
4use crate::cli::init::InitCommandOptions;
5use crate::error::{AddContext, GRError};
6use crate::remote::ConfigFilePath;
7use crate::Result;
8
9const CONFIG_TEMPLATE: &str = r#"
10# Fill in the <VALUE> below with your own values
11# and tweak accordingly.
12
13# NOTE: Substitute domain '.' for '_' in the section name
14# Ex. if DOMAIN is gitlab.com -> [gitlab_com]
15# Ex. if DOMAIN is gitlab.example.com -> [gitlab_example_com]
16
17[<DOMAIN>]
18
19api_token="<VALUE>"
20cache_location=".cache/gitar"
21
22
23# Rate limit remaining threshold. Threshold by which the tool will stop
24# processing requests. Defaults to 10 if not provided. The remote has a counter
25# that decreases with each request. When we reach this threshold we stop for safety.
26# When it reaches 0 the remote will throw errors.
27rate_limit_remaining_threshold=10
28
29[<DOMAIN>.merge_requests]
30
31# Single string username or map { "username" = "your_username", "id" = "your_user_id" }
32preferred_assignee_username="<VALUE>"
33description_signature=""
34
35# Array of usernames if the remote is Github
36# Array of hashmaps username => user ID if the remote is Gitlab or Github
37# Ex:
38# members = ["user1", "user2"]
39# members = [{"username" = "user1", "id" = "1234"}, {"username" = "user2", "id" = "5678"}]
40
41# You can use `gr pj members --format toml` to get the list of members for a project.
42# Be aware that if there are lots of members, this can involve multiple HTTP calls. Make use of
43# `gr pj members --num-pages` to query how many HTTP calls that might involve first.
44# To get your user ID, use `gr us get <your_username> --format toml`. Check the manual for more
45# information.
46
47
48members = []
49
50[<DOMAIN>.cache_expirations]
51
52# Expire read merge requests in 5 minutes
53merge_request="5m"
54# Expire read project metadata, members of a project in 5 days
55project="5d"
56# Pipelines are read often, change often, so expire soon.
57pipeline="30s"
58# Container registry operations including listing image tags and repos
59container_registry="1h"
60# Expire read releases in 1 day
61release="1d"
62# Expire single page calls in 1 day. Ex. Trending repositories in github.com
63single_page="1d"
64# Expire your user gists in 1 day
65gist="1d"
66# Expire repository tags immediately
67repository_tags="0s"
68
69[<DOMAIN>.max_pages_api]
70
71# Get up to 10 pages of merge requests when listing
72merge_request=10
73# Get up to 5 pages of project metadata, members of a project when listing
74project=5
75# Get up to 10 pages of pipelines when listing
76pipeline=10
77# Get up to 10 pages of container registry repositories when listing
78container_registry=10
79# Get up to 10 pages of releases when listing
80release=10
81# Get up to 5 pages of your gists
82gist=5
83# Get up to 10 pages of tags when listing
84repository_tags=10
85
86### Other domains - add more if needed
87"#;
88
89pub fn execute(options: InitCommandOptions, config_path: ConfigFilePath) -> Result<()> {
90    let file = OpenOptions::new()
91        .write(true)
92        .create_new(true)
93        .open(config_path.file_name());
94
95    let mut file = match file {
96        Ok(f) => f,
97        Err(e) if e.kind() == ErrorKind::AlreadyExists => {
98            return Err(GRError::PreconditionNotMet(format!(
99                "Config file at {} already exists, move it aside before running `init` again",
100                config_path.file_name().display()
101            ))
102            .into())
103        }
104        Err(e) => {
105            return Err(e).err_context(format!(
106                "Unable to create config file at path {}",
107                config_path.file_name().display()
108            ))
109        }
110    };
111    generate_and_persist(options, &mut file).err_context(format!(
112        "Failed to generate and persist config at path {}",
113        config_path.file_name().display()
114    ))
115}
116
117fn generate_and_persist<W: Write>(options: InitCommandOptions, writer: &mut W) -> Result<()> {
118    let data = change_placeholders(&options.domain);
119    persist_config(data, writer)
120}
121
122fn persist_config<D: Into<String>, W: Write>(data: D, writer: &mut W) -> Result<()> {
123    writer
124        .write_all(data.into().as_bytes())
125        .err_context("Writing the data to disk failed")?;
126    Ok(())
127}
128
129fn change_placeholders(domain: &str) -> String {
130    let domain = domain.replace(".", "_");
131    CONFIG_TEMPLATE.replace("<DOMAIN>", &domain)
132}
133
134#[cfg(test)]
135mod test {
136
137    use super::*;
138
139    #[test]
140    fn test_persist_config() {
141        let options = InitCommandOptions {
142            domain: "gitweb.com".to_string(),
143        };
144        let mut writer = Vec::new();
145        let result = generate_and_persist(options, &mut writer);
146        assert!(result.is_ok());
147        assert!(!writer.is_empty());
148        let content = String::from_utf8(writer).unwrap();
149        assert!(content.contains("gitweb_com"));
150    }
151}