Skip to main content

sqry_core/git/
nogit.rs

1//! `NoGit` fallback backend
2//!
3//! This backend is used when git is unavailable or disabled.
4//! It always reports no changes, causing the system to fall back
5//! to hash-based change detection.
6
7use super::{ChangeSet, GitBackend, GitCapabilities, Result};
8use std::path::{Path, PathBuf};
9
10/// No-op git backend that always reports no changes
11///
12/// This backend is used when:
13/// - Git binary is not found in PATH
14/// - User sets `SQRY_GIT_BACKEND=none`
15/// - Directory is not a git repository
16///
17/// It allows the rest of the system to gracefully degrade to
18/// hash-based change detection without special-casing git absence.
19#[derive(Debug, Clone, Copy, Default)]
20pub struct NoGit;
21
22impl GitBackend for NoGit {
23    fn is_repo(&self, _root: &Path) -> Result<bool> {
24        // NoGit backend always reports "not a repo"
25        Ok(false)
26    }
27
28    fn repo_root(&self, root: &Path) -> Result<PathBuf> {
29        // Return the input path as-is since there's no git root
30        Ok(root.to_path_buf())
31    }
32
33    fn head(&self, _root: &Path) -> Result<Option<String>> {
34        // No git means no HEAD
35        Ok(None)
36    }
37
38    fn uncommitted(
39        &self,
40        _root: &Path,
41        _include_untracked: bool,
42    ) -> Result<(ChangeSet, Option<String>)> {
43        // No changes, no HEAD
44        Ok((ChangeSet::new(), None))
45    }
46
47    fn since(
48        &self,
49        _root: &Path,
50        _baseline: &str,
51        _rename_similarity: u8,
52    ) -> Result<(ChangeSet, Option<String>)> {
53        // No changes, no HEAD
54        Ok((ChangeSet::new(), None))
55    }
56
57    fn capabilities(&self) -> GitCapabilities {
58        // NoGit supports nothing
59        GitCapabilities {
60            supports_blame: false,
61            supports_time_travel: false,
62            supports_history_index: false,
63        }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_nogit_is_repo() {
73        let backend = NoGit;
74        let result = backend.is_repo(Path::new("/any/path"));
75        assert!(result.is_ok());
76        assert!(!result.unwrap());
77    }
78
79    #[test]
80    fn test_nogit_repo_root() {
81        let backend = NoGit;
82        let test_path = Path::new("/test/path");
83        let result = backend.repo_root(test_path);
84        assert!(result.is_ok());
85        assert_eq!(result.unwrap(), test_path);
86    }
87
88    #[test]
89    fn test_nogit_head() {
90        let backend = NoGit;
91        let result = backend.head(Path::new("/any/path"));
92        assert!(result.is_ok());
93        assert_eq!(result.unwrap(), None);
94    }
95
96    #[test]
97    fn test_nogit_uncommitted() {
98        let backend = NoGit;
99        let (changes, head) = backend.uncommitted(Path::new("/any/path"), true).unwrap();
100        assert!(changes.is_empty());
101        assert_eq!(head, None);
102    }
103
104    #[test]
105    fn test_nogit_since() {
106        let backend = NoGit;
107        let (changes, head) = backend.since(Path::new("/any/path"), "abc123", 50).unwrap();
108        assert!(changes.is_empty());
109        assert_eq!(head, None);
110    }
111
112    #[test]
113    fn test_nogit_capabilities() {
114        let backend = NoGit;
115        let caps = backend.capabilities();
116        assert!(!caps.supports_blame);
117        assert!(!caps.supports_time_travel);
118        assert!(!caps.supports_history_index);
119    }
120}