diff --git a/src/keys.rs b/src/keys.rs index a0be833..aca310b 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -42,35 +42,6 @@ impl KeyInfo { Ok(()) } - pub fn get_current(&self) -> eyre::Result<(HostInfo<'_>, RepoInfo)> { - let repo = git2::Repository::open(".")?; - let remote_url = get_remote(&repo)?; - let login_info = self.get_login(&remote_url)?; - - let mut path = remote_url - .path_segments() - .ok_or_else(|| eyre!("bad path"))? - .collect::>(); - let repo_name = path - .pop() - .ok_or_else(|| eyre!("path does not have repo name"))? - .to_string(); - let owner = path - .pop() - .ok_or_else(|| eyre!("path does not have owner name"))? - .to_string(); - let base_path = path.join("/"); - - let mut url = remote_url; - url.set_path(&base_path); - let host_info = HostInfo { url, login_info }; - let repo_info = RepoInfo { - owner, - name: repo_name, - }; - Ok((host_info, repo_info)) - } - pub fn get_login(&self, url: &Url) -> eyre::Result<&LoginInfo> { let host_str = url .host_str() @@ -87,59 +58,10 @@ impl KeyInfo { .ok_or_else(|| eyre!("not signed in to {domain}"))?; Ok(login_info) } -} -pub struct HostInfo<'a> { - url: Url, - login_info: &'a LoginInfo, -} - -impl<'a> HostInfo<'a> { - pub fn api(&self) -> Result { - self.login_info.api_for(self.url()) + pub fn get_api(&self, url: &Url) -> eyre::Result { + self.get_login(url)?.api_for(url).map_err(Into::into) } - - pub fn url(&self) -> &Url { - &self.url - } - - pub fn username(&self) -> &'a str { - &self.login_info.name - } -} - -pub struct RepoInfo { - owner: String, - name: String, -} - -impl RepoInfo { - pub fn owner(&self) -> &str { - &self.owner - } - - pub fn name(&self) -> &str { - &self.name - } -} - -fn repo_from_url(url: &Url) -> eyre::Result<&str> { - let mut iter = url - .path_segments() - .ok_or_else(|| eyre!("failed to get path from url"))?; - soft_assert::soft_assert!( - matches!(iter.next(), Some(_)), - Err(eyre!("path should have 2 segments, has none")) - ); - let repo = iter - .next() - .ok_or_else(|| eyre!("path should have 2 segments, has only 1"))?; - let repo = repo.strip_suffix(".git").unwrap_or(repo); - soft_assert::soft_assert!( - matches!(iter.next(), None), - Err(eyre!("path should have 2 segments, has more")) - ); - Ok(repo) } #[derive(serde::Serialize, serde::Deserialize, Clone, Default)] @@ -161,15 +83,3 @@ impl LoginInfo { forgejo_api::Forgejo::new(&self.key, url.clone()) } } - -fn get_remote(repo: &git2::Repository) -> eyre::Result { - let head = repo.head()?; - let branch_name = head.name().ok_or_else(|| eyre!("branch name not UTF-8"))?; - let remote_name = repo.branch_upstream_remote(branch_name)?; - let remote_name = remote_name - .as_str() - .ok_or_else(|| eyre!("remote name not UTF-8"))?; - let remote = repo.find_remote(remote_name)?; - let url = Url::parse(std::str::from_utf8(remote.url_bytes())?)?; - Ok(url) -} diff --git a/src/main.rs b/src/main.rs index e53b7d1..cb8e8e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,13 +39,11 @@ async fn main() -> eyre::Result<()> { Command::Repo(repo_subcommand) => repo_subcommand.run(&keys).await?, Command::User { host } => { let host = host.map(|host| Url::parse(&host)).transpose()?; - let (url, name) = match host { - Some(url) => (keys.get_login(&url)?.username(), url), - None => { - let (host, _) = keys.get_current()?; - (host.username(), host.url().clone()) - } + let url = match host { + Some(url) => url, + None => repo::RepoInfo::get_current()?.url().clone(), }; + let name = keys.get_login(&url)?.username(); eprintln!("currently signed in to {name}@{url}"); } Command::Auth(auth_subcommand) => auth_subcommand.run(&mut keys).await?, diff --git a/src/repo.rs b/src/repo.rs index f82ee97..4d0b572 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -1,7 +1,66 @@ use clap::Subcommand; +use eyre::eyre; use forgejo_api::CreateRepoOption; use url::Url; +pub struct RepoInfo { + owner: String, + name: String, + url: Url, +} + +impl RepoInfo { + pub fn get_current() -> eyre::Result { + let repo = git2::Repository::open(".")?; + let url = get_remote(&repo)?; + + let mut path = url.path_segments().ok_or_else(|| eyre!("bad path"))?; + let owner = path + .next() + .ok_or_else(|| eyre!("path does not have owner name"))? + .to_string(); + let name = path + .next() + .ok_or_else(|| eyre!("path does not have repo name"))?; + let name = name.strip_suffix(".git").unwrap_or(name).to_string(); + + let repo_info = RepoInfo { owner, name, url }; + Ok(repo_info) + } + pub fn owner(&self) -> &str { + &self.owner + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn url(&self) -> &Url { + &self.url + } + + pub fn host_url(&self) -> Url { + let mut url = self.url.clone(); + url.path_segments_mut() + .expect("invalid url: cannot be a base") + .pop() + .pop(); + url + } +} + +fn get_remote(repo: &git2::Repository) -> eyre::Result { + let head = repo.head()?; + let branch_name = head.name().ok_or_else(|| eyre!("branch name not UTF-8"))?; + let remote_name = repo.branch_upstream_remote(branch_name)?; + let remote_name = remote_name + .as_str() + .ok_or_else(|| eyre!("remote name not UTF-8"))?; + let remote = repo.find_remote(remote_name)?; + let url = Url::parse(std::str::from_utf8(remote.url_bytes())?)?; + Ok(url) +} + #[derive(Subcommand, Clone, Debug)] pub enum RepoCommand { Create { @@ -73,8 +132,8 @@ impl RepoCommand { } } RepoCommand::Info => { - let (host, repo) = keys.get_current()?; - let api = host.api()?; + let repo = RepoInfo::get_current()?; + let api = keys.get_api(&repo.host_url())?; let repo = api.get_repo(repo.owner(), repo.name()).await?; match repo { Some(repo) => { @@ -84,8 +143,8 @@ impl RepoCommand { } } RepoCommand::Browse => { - let (host, repo) = keys.get_current()?; - let mut url = host.url().clone(); + let repo = RepoInfo::get_current()?; + let mut url = repo.host_url().clone(); let new_path = format!( "{}/{}/{}", url.path().strip_suffix("/").unwrap_or(url.path()),