1use std::time::SystemTime;
4
5use anyhow::Result;
6use endringer_core::backend::VcsBackend;
7use endringer_core::types::{BlameEntry, BranchInfo, CommitId, CommitInfo, DiffSummary, SortOrder, StashEntry, StatusDigest, SubmoduleInfo, TagInfo, WorktreeInfo, WorktreeStatus};
8
9use crate::{blame, branch, commit, diff, graph, object, stash, status, submodule, tag, worktree};
10
11pub struct GitBackend {
18 inner: gix::ThreadSafeRepository,
19}
20
21impl GitBackend {
22 pub fn open(path: &std::path::Path) -> Result<Self> {
27 let inner = gix::discover(path)?.into_sync();
28 Ok(GitBackend { inner })
29 }
30}
31
32macro_rules! repo {
36 ($self:expr) => {
37 $self.inner.to_thread_local()
38 };
39}
40
41impl VcsBackend for GitBackend {
42 fn status_digest(&self) -> Result<StatusDigest> {
43 commit::status_digest(&repo!(self))
44 }
45
46 fn local_branches(&self) -> Result<Vec<BranchInfo>> {
47 branch::local_branches(&repo!(self))
48 }
49
50 fn remote_branches(&self) -> Result<Vec<BranchInfo>> {
51 branch::remote_branches(&repo!(self))
52 }
53
54 fn list_commits(&self) -> Result<Vec<CommitInfo>> {
55 branch::list_commits(&repo!(self))
56 }
57
58 fn list_commits_sorted(&self, order: SortOrder) -> Result<Vec<CommitInfo>> {
59 branch::list_commits_sorted(&repo!(self), order)
60 }
61
62 fn log_since(&self, since: SystemTime, until: SystemTime) -> Result<Vec<CommitInfo>> {
63 branch::log_since(&repo!(self), since, until)
64 }
65
66 fn find_commit(&self, id: &CommitId) -> Result<CommitInfo> {
67 branch::find_commit(&repo!(self), id)
68 }
69
70 fn list_tags(&self) -> Result<Vec<TagInfo>> {
71 tag::list_tags(&repo!(self))
72 }
73
74 fn list_tags_sorted(&self, order: SortOrder) -> Result<Vec<TagInfo>> {
75 tag::list_tags_sorted(&repo!(self), order)
76 }
77
78 fn create_tag(&self, name: &str) -> Result<()> {
79 tag::create_tag(&repo!(self), name)
80 }
81
82 fn create_annotated_tag(&self, name: &str, message: &str) -> Result<()> {
83 tag::create_annotated_tag(&repo!(self), name, message)
84 }
85
86 fn delete_tag(&self, name: &str) -> Result<()> {
87 tag::delete_tag(&repo!(self), name)
88 }
89
90 fn diff(&self, from: &CommitId, to: &CommitId) -> Result<DiffSummary> {
91 diff::diff(&repo!(self), from, to)
92 }
93
94 fn remote_url(&self, name: &str) -> Option<String> {
95 let repo = repo!(self);
96 let remote = repo.find_remote(name).ok()?;
97 let url = remote.url(gix::remote::Direction::Fetch)?;
98 Some(url.to_bstring().to_string())
99 }
100
101 fn is_dirty(&self) -> Result<bool> {
102 status::is_dirty(&repo!(self))
103 }
104
105 fn merge_base(&self, a: &CommitId, b: &CommitId) -> Result<Option<CommitId>> {
106 graph::merge_base(&repo!(self), a, b)
107 }
108
109 fn is_ancestor(&self, candidate: &CommitId, descendant: &CommitId) -> Result<bool> {
110 graph::is_ancestor(&repo!(self), candidate, descendant)
111 }
112
113 fn blame(&self, path: &std::path::Path) -> Result<Vec<BlameEntry>> {
114 blame::blame(&repo!(self), path)
115 }
116
117 fn worktree_status(&self) -> Result<WorktreeStatus> {
118 status::worktree_status(&repo!(self))
119 }
120
121 fn file_at_commit(&self, path: &std::path::Path, commit_id: &CommitId) -> Result<Vec<u8>> {
122 object::file_at_commit(&repo!(self), path, commit_id)
123 }
124
125 fn submodules(&self) -> Result<Vec<SubmoduleInfo>> {
126 submodule::submodules(&repo!(self))
127 }
128
129 fn stash_entries(&self) -> Result<Vec<StashEntry>> {
130 stash::stash_entries(&repo!(self))
131 }
132
133 fn worktrees(&self) -> Result<Vec<WorktreeInfo>> {
134 worktree::worktrees(&repo!(self))
135 }
136}