1mod implementation;
2pub mod reserved_state;
3mod templates;
4#[cfg(test)]
5mod tests;
6
7use super::*;
8use eyre::Result;
9use git2::{
10 ApplyLocation, BranchType, DiffFormat, Email, EmailCreateOptions, IndexAddOption, ObjectType,
11 Oid, Repository, RepositoryInitOptions, ResetType, Sort, StashApplyOptions, StashFlags, Status,
12 StatusOptions, StatusShow,
13};
14use implementation::RawRepositoryInner;
15use simperby_core::reserved::ReservedState;
16use std::convert::TryFrom;
17use std::str;
18use templates::*;
19use thiserror::Error;
20
21#[derive(Error, Debug)]
22pub enum Error {
23 #[error("git2 error: {0}")]
24 Git2Error(git2::Error),
25 #[error("not found: {0}")]
27 NotFound(String),
28 #[error("the repository is invalid: {0}")]
31 InvalidRepository(String),
32 #[error("unknown error: {0}")]
33 Unknown(String),
34}
35
36impl From<git2::Error> for Error {
37 fn from(e: git2::Error) -> Self {
38 Error::Git2Error(e)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct SemanticCommit {
47 pub title: String,
48 pub body: String,
49 pub diff: Diff,
50 pub author: MemberName,
51 pub timestamp: Timestamp,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
61pub struct RawCommit {
62 pub message: String,
63 pub diff: Option<String>,
64 pub author: String,
65 pub email: String,
66 pub timestamp: Timestamp,
67}
68
69#[derive(Debug)]
70pub struct RawRepository {
71 inner: tokio::sync::Mutex<Option<RawRepositoryInner>>,
72}
73
74impl RawRepository {
75 pub async fn init(
79 directory: &str,
80 init_commit_message: &str,
81 init_commit_branch: &Branch,
82 ) -> Result<Self, Error>
83 where
84 Self: Sized,
85 {
86 let repo = RawRepositoryInner::init(directory, init_commit_message, init_commit_branch)?;
87 let inner = tokio::sync::Mutex::new(Some(repo));
88
89 Ok(Self { inner })
90 }
91
92 pub async fn open(directory: &str) -> Result<Self, Error>
94 where
95 Self: Sized,
96 {
97 let repo = RawRepositoryInner::open(directory)?;
98 let inner = tokio::sync::Mutex::new(Some(repo));
99
100 Ok(Self { inner })
101 }
102
103 pub async fn clone(directory: &str, url: &str) -> Result<Self, Error>
107 where
108 Self: Sized,
109 {
110 let repo = RawRepositoryInner::clone(directory, url)?;
111 let inner = tokio::sync::Mutex::new(Some(repo));
112
113 Ok(Self { inner })
114 }
115
116 pub async fn retrieve_commit_hash(
120 &self,
121 revision_selection: String,
122 ) -> Result<CommitHash, Error> {
123 helper_1(
124 self,
125 RawRepositoryInner::retrieve_commit_hash,
126 revision_selection,
127 )
128 .await
129 }
130
131 pub async fn list_branches(&self) -> Result<Vec<Branch>, Error> {
137 helper_0(self, RawRepositoryInner::list_branches).await
138 }
139
140 pub async fn create_branch(
142 &self,
143 branch_name: Branch,
144 commit_hash: CommitHash,
145 ) -> Result<(), Error> {
146 helper_2(
147 self,
148 RawRepositoryInner::create_branch,
149 branch_name,
150 commit_hash,
151 )
152 .await
153 }
154
155 pub async fn locate_branch(&self, branch: Branch) -> Result<CommitHash, Error> {
157 helper_1(self, RawRepositoryInner::locate_branch, branch).await
158 }
159
160 pub async fn get_branches(&self, commit_hash: CommitHash) -> Result<Vec<Branch>, Error> {
162 helper_1(self, RawRepositoryInner::get_branches, commit_hash).await
163 }
164
165 pub async fn move_branch(
167 &mut self,
168 branch: Branch,
169 commit_hash: CommitHash,
170 ) -> Result<(), Error> {
171 helper_2_mut(self, RawRepositoryInner::move_branch, branch, commit_hash).await
172 }
173
174 pub async fn delete_branch(&mut self, branch: Branch) -> Result<(), Error> {
176 helper_1_mut(self, RawRepositoryInner::delete_branch, branch).await
177 }
178
179 pub async fn list_tags(&self) -> Result<Vec<Tag>, Error> {
185 helper_0(self, RawRepositoryInner::list_tags).await
186 }
187
188 pub async fn create_tag(&mut self, tag: Tag, commit_hash: CommitHash) -> Result<(), Error> {
190 helper_2_mut(self, RawRepositoryInner::create_tag, tag, commit_hash).await
191 }
192
193 pub async fn locate_tag(&self, tag: Tag) -> Result<CommitHash, Error> {
195 helper_1(self, RawRepositoryInner::locate_tag, tag).await
196 }
197
198 pub async fn get_tag(&self, commit_hash: CommitHash) -> Result<Vec<Tag>, Error> {
200 helper_1(self, RawRepositoryInner::get_tag, commit_hash).await
201 }
202
203 pub async fn remove_tag(&mut self, tag: Tag) -> Result<(), Error> {
205 helper_1_mut(self, RawRepositoryInner::remove_tag, tag).await
206 }
207
208 pub async fn create_commit(&mut self, commit: RawCommit) -> Result<CommitHash, Error> {
217 helper_1_mut(self, RawRepositoryInner::create_commit, commit).await
218 }
219
220 pub async fn create_commit_all(&mut self, commit: RawCommit) -> Result<CommitHash, Error> {
225 helper_1_mut(self, RawRepositoryInner::create_commit_all, commit).await
226 }
227
228 pub async fn read_commit(&self, commit_hash: CommitHash) -> Result<RawCommit, Error> {
230 helper_1(self, RawRepositoryInner::read_commit, commit_hash).await
231 }
232
233 pub async fn create_semantic_commit(
239 &mut self,
240 commit: SemanticCommit,
241 authored_by_simperby: bool,
242 ) -> Result<CommitHash, Error> {
243 helper_2_mut(
244 self,
245 RawRepositoryInner::create_semantic_commit,
246 commit,
247 authored_by_simperby,
248 )
249 .await
250 }
251
252 pub async fn read_semantic_commit(
254 &self,
255 commit_hash: CommitHash,
256 ) -> Result<SemanticCommit, Error> {
257 helper_1(self, RawRepositoryInner::read_semantic_commit, commit_hash).await
258 }
259
260 pub async fn run_garbage_collection(&mut self) -> Result<(), Error> {
262 helper_0_mut(self, RawRepositoryInner::run_garbage_collection).await
263 }
264
265 pub async fn checkout_clean(&mut self) -> Result<(), Error> {
272 helper_0_mut(self, RawRepositoryInner::checkout_clean).await
273 }
274
275 pub async fn checkout(&mut self, branch: Branch) -> Result<(), Error> {
277 helper_1_mut(self, RawRepositoryInner::checkout, branch).await
278 }
279
280 pub async fn checkout_detach(&mut self, commit_hash: CommitHash) -> Result<(), Error> {
282 helper_1_mut(self, RawRepositoryInner::checkout_detach, commit_hash).await
283 }
284
285 pub async fn stash(&mut self) -> Result<(), Error> {
287 helper_0_mut(self, RawRepositoryInner::stash).await
288 }
289
290 pub async fn stash_pop(&mut self, index: bool) -> Result<(), Error> {
293 helper_1_mut(self, RawRepositoryInner::stash_pop, index).await
294 }
295
296 pub async fn stash_apply(&mut self, index: bool) -> Result<(), Error> {
299 helper_1_mut(self, RawRepositoryInner::stash_apply, index).await
300 }
301
302 pub async fn stash_drop(&mut self) -> Result<(), Error> {
304 helper_0_mut(self, RawRepositoryInner::stash_drop).await
305 }
306
307 pub async fn check_clean(&self) -> Result<(), Error> {
309 helper_0(self, RawRepositoryInner::check_clean).await
310 }
311
312 pub async fn get_working_directory_path(&self) -> Result<String, Error> {
318 helper_0(self, RawRepositoryInner::get_working_directory_path).await
319 }
320
321 pub async fn get_head(&self) -> Result<CommitHash, Error> {
323 helper_0(self, RawRepositoryInner::get_head).await
324 }
325
326 pub async fn get_currently_checkout_branch(&self) -> Result<Option<Branch>, Error> {
329 helper_0(self, RawRepositoryInner::get_currently_checkout_branch).await
330 }
331
332 pub async fn get_initial_commit(&self) -> Result<CommitHash, Error> {
336 helper_0(self, RawRepositoryInner::get_initial_commit).await
337 }
338
339 pub async fn get_patch(&self, commit_hash: CommitHash) -> Result<String, Error> {
341 helper_1(self, RawRepositoryInner::get_patch, commit_hash).await
342 }
343
344 pub async fn show_commit(&self, commit_hash: CommitHash) -> Result<String, Error> {
346 helper_1(self, RawRepositoryInner::show_commit, commit_hash).await
347 }
348
349 pub async fn list_ancestors(
354 &self,
355 commit_hash: CommitHash,
356 max: Option<usize>,
357 ) -> Result<Vec<CommitHash>, Error> {
358 helper_2(self, RawRepositoryInner::list_ancestors, commit_hash, max).await
359 }
360
361 pub async fn query_commit_path(
367 &self,
368 ancestor: CommitHash,
369 descendant: CommitHash,
370 ) -> Result<Vec<CommitHash>, Error> {
371 helper_2(
372 self,
373 RawRepositoryInner::query_commit_path,
374 ancestor,
375 descendant,
376 )
377 .await
378 }
379
380 pub async fn list_children(&self, commit_hash: CommitHash) -> Result<Vec<CommitHash>, Error> {
382 helper_1(self, RawRepositoryInner::list_children, commit_hash).await
383 }
384
385 pub async fn find_merge_base(
387 &self,
388 commit_hash1: CommitHash,
389 commit_hash2: CommitHash,
390 ) -> Result<CommitHash, Error> {
391 helper_2(
392 self,
393 RawRepositoryInner::find_merge_base,
394 commit_hash1,
395 commit_hash2,
396 )
397 .await
398 }
399
400 pub async fn read_reserved_state(&self) -> Result<ReservedState, Error> {
402 helper_0(self, RawRepositoryInner::read_reserved_state).await
403 }
404
405 pub async fn read_reserved_state_at_commit(
407 &self,
408 commit_hash: CommitHash,
409 ) -> Result<ReservedState, Error> {
410 helper_1(
411 self,
412 RawRepositoryInner::read_reserved_state_at_commit,
413 commit_hash,
414 )
415 .await
416 }
417
418 pub async fn add_remote(
424 &mut self,
425 remote_name: String,
426 remote_url: String,
427 ) -> Result<(), Error> {
428 helper_2_mut(
429 self,
430 RawRepositoryInner::add_remote,
431 remote_name,
432 remote_url,
433 )
434 .await
435 }
436
437 pub async fn remove_remote(&mut self, remote_name: String) -> Result<(), Error> {
439 helper_1_mut(self, RawRepositoryInner::remove_remote, remote_name).await
440 }
441
442 pub async fn fetch_all(&mut self, prune: bool) -> Result<(), Error> {
444 helper_1_mut(self, RawRepositoryInner::fetch_all, prune).await
445 }
446
447 pub async fn push_option(
450 &self,
451 remote_name: String,
452 branch: Branch,
453 option: Option<String>,
454 ) -> Result<(), Error> {
455 helper_3(
456 self,
457 RawRepositoryInner::push_option,
458 remote_name,
459 branch,
460 option,
461 )
462 .await
463 }
464
465 pub async fn ping_remote(&self, remote_name: String) -> Result<bool, Error> {
467 helper_1(self, RawRepositoryInner::ping_remote, remote_name).await
468 }
469
470 pub async fn list_remotes(&self) -> Result<Vec<(String, String)>, Error> {
474 helper_0(self, RawRepositoryInner::list_remotes).await
475 }
476
477 pub async fn list_remote_tracking_branches(
481 &self,
482 ) -> Result<Vec<(String, String, CommitHash)>, Error> {
483 helper_0(self, RawRepositoryInner::list_remote_tracking_branches).await
484 }
485
486 pub async fn locate_remote_tracking_branch(
488 &self,
489 remote_name: String,
490 branch_name: String,
491 ) -> Result<CommitHash, Error> {
492 helper_2(
493 self,
494 RawRepositoryInner::locate_remote_tracking_branch,
495 remote_name,
496 branch_name,
497 )
498 .await
499 }
500}
501
502#[cfg(target_os = "windows")]
503pub fn run_command(command: impl AsRef<str>) -> Result<(), Error> {
504 println!("> RUN: {}", command.as_ref());
505 let mut child = std::process::Command::new("C:/Program Files/Git/bin/sh.exe")
506 .arg("--login")
507 .arg("-c")
508 .arg(command.as_ref())
509 .spawn()
510 .map_err(|_| Error::Unknown("failed to execute process".to_string()))?;
511 let ecode = child
512 .wait()
513 .map_err(|_| Error::Unknown("failed to wait on child".to_string()))?;
514
515 if ecode.success() {
516 Ok(())
517 } else {
518 Err(Error::Unknown("failed to run process".to_string()))
519 }
520}
521
522#[cfg(not(target_os = "windows"))]
523pub fn run_command(command: impl AsRef<str>) -> Result<(), Error> {
524 println!("> RUN: {}", command.as_ref());
525 let mut child = std::process::Command::new("sh")
526 .arg("-c")
527 .arg(command.as_ref())
528 .spawn()
529 .map_err(|_| Error::Unknown("failed to execute process".to_string()))?;
530
531 let ecode = child
532 .wait()
533 .map_err(|_| Error::Unknown("failed to wait on child".to_string()))?;
534
535 if ecode.success() {
536 Ok(())
537 } else {
538 Err(Error::Unknown("failed to run process".to_string()))
539 }
540}