Skip to main content

endringer_async/
async_api.rs

1//! [`AsyncRepository`] — async wrapper around [`endringer::repository::Repository`].
2
3use std::path::Path;
4use std::sync::Arc;
5use std::time::SystemTime;
6
7use anyhow::Result;
8use endringer::repository::{Repository, jj_repository, repository};
9use endringer::{
10    AheadBehind, BlameEntry, BranchInfo, BranchTrackingInfo, CommitId, CommitInfo,
11    DiffSummary, RepositoryInfo, SortOrder, StashEntry, StatusDigest, SubmoduleInfo,
12    TagInfo, WorktreeInfo, WorktreeStatus,
13};
14
15/// Async wrapper around [`Repository`].
16///
17/// Cloneable. Each method clones the inner `Arc<Repository>` and dispatches
18/// to `tokio::task::spawn_blocking` so that blocking VCS I/O does not stall
19/// the async executor.
20#[derive(Clone)]
21pub struct AsyncRepository {
22    inner: Arc<Repository>,
23}
24
25impl AsyncRepository {
26    /// Opens a Git repository at `path`.
27    pub async fn open(path: &Path) -> Result<Self> {
28        let path = path.to_path_buf();
29        let inner = tokio::task::spawn_blocking(move || repository(&path)).await??;
30        Ok(AsyncRepository { inner: Arc::new(inner) })
31    }
32
33    /// Opens a Jujutsu repository at `path`.
34    pub async fn open_jj(path: &Path) -> Result<Self> {
35        let path = path.to_path_buf();
36        let inner = tokio::task::spawn_blocking(move || jj_repository(&path)).await??;
37        Ok(AsyncRepository { inner: Arc::new(inner) })
38    }
39
40    // ── Status ─────────────────────────────────────────────────────────── //
41
42    pub async fn status_digest(&self) -> Result<StatusDigest> {
43        let r = Arc::clone(&self.inner);
44        tokio::task::spawn_blocking(move || r.status_digest()).await?
45    }
46
47    // ── Branches ───────────────────────────────────────────────────────── //
48
49    pub async fn local_branches(&self) -> Result<Vec<BranchInfo>> {
50        let r = Arc::clone(&self.inner);
51        tokio::task::spawn_blocking(move || r.local_branches()).await?
52    }
53
54    pub async fn remote_branches(&self) -> Result<Vec<BranchInfo>> {
55        let r = Arc::clone(&self.inner);
56        tokio::task::spawn_blocking(move || r.remote_branches()).await?
57    }
58
59    // ── Commits ────────────────────────────────────────────────────────── //
60
61    pub async fn list_commits(&self) -> Result<Vec<CommitInfo>> {
62        let r = Arc::clone(&self.inner);
63        tokio::task::spawn_blocking(move || r.list_commits()).await?
64    }
65
66    pub async fn list_commits_sorted(&self, order: SortOrder) -> Result<Vec<CommitInfo>> {
67        let r = Arc::clone(&self.inner);
68        tokio::task::spawn_blocking(move || r.list_commits_sorted(order)).await?
69    }
70
71    pub async fn log_since(&self, since: SystemTime, until: SystemTime) -> Result<Vec<CommitInfo>> {
72        let r = Arc::clone(&self.inner);
73        tokio::task::spawn_blocking(move || r.log_since(since, until)).await?
74    }
75
76    pub async fn find_commit(&self, id: CommitId) -> Result<CommitInfo> {
77        let r = Arc::clone(&self.inner);
78        tokio::task::spawn_blocking(move || r.find_commit(&id)).await?
79    }
80
81    // ── Tags ───────────────────────────────────────────────────────────── //
82
83    pub async fn list_tags(&self) -> Result<Vec<TagInfo>> {
84        let r = Arc::clone(&self.inner);
85        tokio::task::spawn_blocking(move || r.list_tags()).await?
86    }
87
88    pub async fn list_tags_sorted(&self, order: SortOrder) -> Result<Vec<TagInfo>> {
89        let r = Arc::clone(&self.inner);
90        tokio::task::spawn_blocking(move || r.list_tags_sorted(order)).await?
91    }
92
93    pub async fn create_tag(&self, name: String) -> Result<()> {
94        let r = Arc::clone(&self.inner);
95        tokio::task::spawn_blocking(move || r.create_tag(&name)).await?
96    }
97
98    pub async fn create_annotated_tag(&self, name: String, message: String) -> Result<()> {
99        let r = Arc::clone(&self.inner);
100        tokio::task::spawn_blocking(move || r.create_annotated_tag(&name, &message)).await?
101    }
102
103    pub async fn delete_tag(&self, name: String) -> Result<()> {
104        let r = Arc::clone(&self.inner);
105        tokio::task::spawn_blocking(move || r.delete_tag(&name)).await?
106    }
107
108    // ── Diff ───────────────────────────────────────────────────────────── //
109
110    pub async fn diff(&self, from: CommitId, to: CommitId) -> Result<DiffSummary> {
111        let r = Arc::clone(&self.inner);
112        tokio::task::spawn_blocking(move || r.diff(&from, &to)).await?
113    }
114
115    // ── Remotes ────────────────────────────────────────────────────────── //
116
117    pub async fn remote_url(&self, name: String) -> Option<String> {
118        let r = Arc::clone(&self.inner);
119        tokio::task::spawn_blocking(move || r.remote_url(&name))
120            .await
121            .ok()
122            .flatten()
123    }
124
125    // ── Working tree ───────────────────────────────────────────────────── //
126
127    pub async fn is_dirty(&self) -> Result<bool> {
128        let r = Arc::clone(&self.inner);
129        tokio::task::spawn_blocking(move || r.is_dirty()).await?
130    }
131
132    // ── Commit graph ───────────────────────────────────────────────────── //
133
134    pub async fn merge_base(&self, a: CommitId, b: CommitId) -> Result<Option<CommitId>> {
135        let r = Arc::clone(&self.inner);
136        tokio::task::spawn_blocking(move || r.merge_base(&a, &b)).await?
137    }
138
139    pub async fn is_ancestor(&self, candidate: CommitId, descendant: CommitId) -> Result<bool> {
140        let r = Arc::clone(&self.inner);
141        tokio::task::spawn_blocking(move || r.is_ancestor(&candidate, &descendant)).await?
142    }
143
144    // ── Blame ──────────────────────────────────────────────────────────── //
145
146    pub async fn blame(&self, path: std::path::PathBuf) -> Result<Vec<BlameEntry>> {
147        let r = Arc::clone(&self.inner);
148        tokio::task::spawn_blocking(move || r.blame(&path)).await?
149    }
150
151    pub async fn worktree_status(&self) -> Result<WorktreeStatus> {
152        let r = Arc::clone(&self.inner);
153        tokio::task::spawn_blocking(move || r.worktree_status()).await?
154    }
155
156    pub async fn file_at_commit(
157        &self,
158        path: std::path::PathBuf,
159        commit_id: CommitId,
160    ) -> Result<Vec<u8>> {
161        let r = Arc::clone(&self.inner);
162        tokio::task::spawn_blocking(move || r.file_at_commit(&path, &commit_id)).await?
163    }
164
165    pub async fn submodules(&self) -> Result<Vec<SubmoduleInfo>> {
166        let r = Arc::clone(&self.inner);
167        tokio::task::spawn_blocking(move || r.submodules()).await?
168    }
169
170    pub async fn stash_entries(&self) -> Result<Vec<StashEntry>> {
171        let r = Arc::clone(&self.inner);
172        tokio::task::spawn_blocking(move || r.stash_entries()).await?
173    }
174
175    pub async fn worktrees(&self) -> Result<Vec<WorktreeInfo>> {
176        let r = Arc::clone(&self.inner);
177        tokio::task::spawn_blocking(move || r.worktrees()).await?
178    }
179
180    // ── Ahead / behind ─────────────────────────────────────────────────── //
181
182    pub async fn ahead_behind(
183        &self,
184        local: CommitId,
185        upstream: CommitId,
186    ) -> Result<AheadBehind> {
187        let r = Arc::clone(&self.inner);
188        tokio::task::spawn_blocking(move || r.ahead_behind(&local, &upstream)).await?
189    }
190
191    pub async fn branch_ahead_behind(
192        &self,
193        branch: String,
194    ) -> Result<Option<AheadBehind>> {
195        let r = Arc::clone(&self.inner);
196        tokio::task::spawn_blocking(move || r.branch_ahead_behind(&branch)).await?
197    }
198
199    // ── Repository info ────────────────────────────────────────────────── //
200
201    pub async fn repository_info(&self) -> Result<RepositoryInfo> {
202        let r = Arc::clone(&self.inner);
203        tokio::task::spawn_blocking(move || r.repository_info()).await?
204    }
205
206    // ── Branch tracking ────────────────────────────────────────────────── //
207
208    pub async fn branch_tracking(&self, branch: String) -> Result<BranchTrackingInfo> {
209        let r = Arc::clone(&self.inner);
210        tokio::task::spawn_blocking(move || r.branch_tracking(&branch)).await?
211    }
212
213    pub async fn local_branch_tracking(&self) -> Result<Vec<BranchTrackingInfo>> {
214        let r = Arc::clone(&self.inner);
215        tokio::task::spawn_blocking(move || r.local_branch_tracking()).await?
216    }
217
218    pub async fn is_merged_into(&self, branch: String, target: String) -> Result<bool> {
219        let r = Arc::clone(&self.inner);
220        tokio::task::spawn_blocking(move || r.is_merged_into(&branch, &target)).await?
221    }
222}