Skip to main content

grit_lib/config/
handle.rs

1//! Repository-scoped configuration reads and local writes via [`ConfigHandle`].
2use std::fs;
3use std::sync::Arc;
4
5use crate::config::{ConfigFile, ConfigScope, ConfigSet};
6use crate::error::Result;
7use crate::repo::Repository;
8
9/// Read merged config and update the repository-local config file.
10#[derive(Debug, Copy, Clone)]
11pub struct ConfigHandle<'repo> {
12    repo: &'repo Repository,
13}
14
15impl<'repo> ConfigHandle<'repo> {
16    /// Bind config operations to `repo`.
17    #[inline]
18    #[must_use]
19    pub fn new(repo: &'repo Repository) -> Self {
20        Self { repo }
21    }
22
23    /// Underlying repository.
24    #[inline]
25    #[must_use]
26    pub fn repository(&self) -> &'repo Repository {
27        self.repo
28    }
29
30    /// Look up a merged config value (system / global / local / worktree cascade).
31    ///
32    /// # Errors
33    ///
34    /// Propagates config load failures.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// # fn main() -> grit_lib::error::Result<()> {
40    /// use grit_lib::repo::{InitOptions, Repository};
41    /// let dir = std::env::temp_dir().join("grit-config-get-doc");
42    /// let repo = Repository::init(&dir, &InitOptions::default())?;
43    /// let _ = repo.config_handle().get("core.repositoryformatversion");
44    /// # Ok(())
45    /// # }
46    /// ```
47    #[inline]
48    pub fn get(&self, key: &str) -> Result<Option<String>> {
49        Ok(self.snapshot()?.get(key))
50    }
51
52    /// Set a key in the repository-local config file and reload the in-memory snapshot.
53    ///
54    /// For linked worktrees this writes `commondir/config` when the per-worktree git directory
55    /// has no `config` file, matching Git's `--local` target.
56    ///
57    /// # Errors
58    ///
59    /// Propagates config parse, validation, and I/O errors.
60    #[inline]
61    pub fn set_local(&self, key: &str, value: &str) -> Result<()> {
62        let path = crate::repo::local_config_write_path(self.repo.git_dir());
63        let content = fs::read_to_string(&path).unwrap_or_default();
64        let mut file = ConfigFile::parse(&path, &content, ConfigScope::Local)?;
65        file.set(key, value)?;
66        file.write()?;
67        self.repo.reload_config()
68    }
69
70    /// Remove `key` from the repository-local config file and reload the snapshot.
71    ///
72    /// # Errors
73    ///
74    /// Propagates config parse, validation, and I/O errors.
75    pub fn unset_local(&self, key: &str) -> Result<()> {
76        let path = crate::repo::local_config_write_path(self.repo.git_dir());
77        let content = fs::read_to_string(&path).unwrap_or_default();
78        let mut file = ConfigFile::parse(&path, &content, ConfigScope::Local)?;
79        file.unset(key)?;
80        file.write()?;
81        self.repo.reload_config()
82    }
83
84    /// Remove an entire section from the repository-local config file.
85    ///
86    /// Section names use dotted form (for example `branch.topic` or `branch.feature/x`).
87    ///
88    /// # Errors
89    ///
90    /// Propagates config parse, validation, and I/O errors.
91    pub fn remove_local_section(&self, section: &str) -> Result<()> {
92        let path = crate::repo::local_config_write_path(self.repo.git_dir());
93        let content = fs::read_to_string(&path).unwrap_or_default();
94        let mut file = ConfigFile::parse(&path, &content, ConfigScope::Local)?;
95        file.remove_section(section)?;
96        file.write()?;
97        self.repo.reload_config()
98    }
99
100    /// Shared merged config snapshot (same as [`Repository::config`]).
101    #[inline]
102    pub fn snapshot(&self) -> Result<Arc<ConfigSet>> {
103        self.repo.config()
104    }
105}