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