1use std::{path::PathBuf, process};
2
3use anyhow::Result;
4use git2::Repository;
5
6mod providers;
7use providers::github::GitHub;
8use providers::Provider;
9
10pub struct GitOpen<'a> {
11 repository: Repository,
13 remote_name: &'a str,
15
16 provider: Box<dyn Provider>,
17}
18
19pub enum Entity {
20 Repository,
21 Branch,
22 Commit,
23 PullRequest,
24}
25
26impl<'a> GitOpen<'a> {
27 pub fn new(path: &PathBuf, remote_name: &'a str) -> Self {
28 let mut cwd = path.clone();
29
30 let repository = loop {
31 match Repository::open(&cwd) {
32 Ok(r) => break r,
33 Err(_) => {
34 if !cwd.pop() {
35 panic!("Unable to open repository at path or parent: {:?}", path);
36 }
37 }
38 }
39 };
40
41 let provider = GitHub {};
43
44 Self {
45 repository,
46 remote_name,
47 provider: Box::new(provider),
48 }
49 }
50
51 pub fn url(&self, entity: Entity) -> Result<String> {
52 let remote = self
53 .repository
54 .find_remote(self.remote_name)
55 .unwrap_or_else(|e| {
56 println!("Could not retrieve remote: {}", e);
57 process::exit(1);
58 });
59
60 let remote_url = remote.url().unwrap_or_else(|| {
61 println!("Could not retrieve remote url.");
62 process::exit(1);
63 });
64
65 let head = self.repository.head().unwrap_or_else(|e| {
66 println!("Could not retrieve repository head: {}", e);
67 process::exit(1);
68 });
69
70 let branch = head.shorthand().unwrap_or_else(|| {
71 println!("Could not retrieve branch name.");
72 process::exit(1);
73 });
74
75 let commit = head.target().unwrap_or_else(|| {
76 println!("Could not retrieve commit.");
77 process::exit(1);
78 });
79
80 match entity {
81 Entity::Repository => self.provider.web_url(remote_url),
82 Entity::Branch => self.provider.branch_url(remote_url, branch),
83 Entity::PullRequest => self.provider.pull_request_url(remote_url, branch),
84 Entity::Commit => self.provider.commit_url(remote_url, &commit.to_string()),
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::GitOpen;
92 use std::{env, path::PathBuf};
93
94 #[test]
95 fn can_be_created() {
96 let p = env::current_dir().unwrap();
97 GitOpen::new(&p, "origin");
98 }
99
100 #[test]
101 fn can_be_created_in_child_path() {
102 let mut p = env::current_dir().unwrap();
103 p.push("src");
104 GitOpen::new(&p, "origin");
105 }
106
107 #[test]
108 #[should_panic]
109 fn panics_correctly() {
110 let p = PathBuf::from("/tmp");
111 GitOpen::new(&p, "origin");
112 }
113}