codeberg_cli/types/
git.rs1use std::path::PathBuf;
2
3use anyhow::Context;
4use git2::{Remote, Repository};
5
6pub struct OwnerRepo {
7 pub owner: String,
8 pub repo: String,
9}
10
11pub struct Git {
12 pub repo: Option<Repository>,
13}
14
15impl Default for Git {
16 fn default() -> Self {
17 Self {
18 repo: Repository::discover("./.").ok(),
19 }
20 }
21}
22
23impl Git {
24 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn remotes(&self) -> anyhow::Result<Vec<Remote>> {
29 let repo = self
30 .repo
31 .as_ref()
32 .context("No repository found in the current path even though one is needed!")?;
33 let remotes = repo
34 .remotes()?
35 .into_iter()
36 .filter_map(|remote| {
37 let remote = remote?;
38 let remote = repo.find_remote(remote).ok()?;
39 Some(remote)
40 })
41 .collect::<Vec<_>>();
42 Ok(remotes)
43 }
44
45 pub fn owner_repo(&self) -> anyhow::Result<OwnerRepo> {
46 let remotes = self
47 .remotes()?
48 .into_iter()
49 .filter_map(|remote| {
50 let mut git_url = remote.url().map(PathBuf::from)?;
51 let repo = git_url
56 .file_name()?
57 .to_str()?
58 .trim_end_matches(".git")
59 .to_owned();
60 git_url.pop();
61
62 let https_owner = git_url
63 .file_name()?
64 .to_str()
65 .filter(|owner| !owner.contains(":"))
66 .map(|owner| owner.to_owned());
67 let ssh_owner = git_url
68 .to_str()?
69 .split_once(":")
70 .map(|(_junk, owner)| owner.to_owned());
71 let remote_name = remote.name()?.to_owned();
72 Some((remote_name, https_owner.or(ssh_owner)?, repo))
73 })
74 .inspect(|x| tracing::debug!("{x:?}"))
75 .collect::<Vec<_>>();
76
77 remotes
78 .iter()
79 .find_map(|(name, owner, repo)| (*name == "origin").then_some((owner, repo)))
80 .or_else(|| remotes.first().map(|(_, owner, repo)| (owner, repo)))
81 .map(|(owner, repo)| OwnerRepo {
82 owner: owner.clone(),
83 repo: repo.clone(),
84 })
85 .context("Couldn't find owner and repo")
86 }
87}