summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCyborus <cyborus@noreply.codeberg.org>2024-04-18 00:19:37 +0200
committerCyborus <cyborus@noreply.codeberg.org>2024-04-18 00:19:37 +0200
commited7f84eb5af63a8bd465af5cae5d6fa829ed22a1 (patch)
tree2c9c77430e88aaaf449eb49880fc44f0220bf559
parentMerge pull request 'update to new version of `forgejo-api`' (#37) from update... (diff)
parentformat (diff)
downloadforgejo-cli-ed7f84eb5af63a8bd465af5cae5d6fa829ed22a1.tar.xz
forgejo-cli-ed7f84eb5af63a8bd465af5cae5d6fa829ed22a1.zip
Merge pull request 'improve host url and repo name detection' (#38) from improve-host-detection into main
Reviewed-on: https://codeberg.org/Cyborus/forgejo-cli/pulls/38
-rw-r--r--src/issues.rs53
-rw-r--r--src/main.rs31
-rw-r--r--src/release.rs77
-rw-r--r--src/repo.rs327
4 files changed, 353 insertions, 135 deletions
diff --git a/src/issues.rs b/src/issues.rs
index 1f1d07d..4a3a708 100644
--- a/src/issues.rs
+++ b/src/issues.rs
@@ -1,14 +1,24 @@
-use clap::Subcommand;
-use eyre::eyre;
+use clap::{Args, Subcommand};
+use eyre::{eyre, OptionExt};
use forgejo_api::structs::{
Comment, CreateIssueCommentOption, CreateIssueOption, EditIssueOption, IssueGetCommentsQuery,
};
use forgejo_api::Forgejo;
-use crate::repo::RepoInfo;
+use crate::repo::{RepoInfo, RepoName};
+
+#[derive(Args, Clone, Debug)]
+pub struct IssueCommand {
+ #[clap(long, short = 'R')]
+ remote: Option<String>,
+ #[clap(long, short)]
+ repo: Option<String>,
+ #[clap(subcommand)]
+ command: IssueSubcommand,
+}
#[derive(Subcommand, Clone, Debug)]
-pub enum IssueCommand {
+pub enum IssueSubcommand {
Create {
title: String,
#[clap(long)]
@@ -86,11 +96,14 @@ pub enum ViewCommand {
}
impl IssueCommand {
- pub async fn run(self, keys: &crate::KeyInfo, remote_name: Option<&str>) -> eyre::Result<()> {
- use IssueCommand::*;
- let repo = RepoInfo::get_current(remote_name)?;
- let api = keys.get_api(&repo.host_url())?;
- match self {
+ pub async fn run(self, keys: &crate::KeyInfo, host_name: Option<&str>) -> eyre::Result<()> {
+ use IssueSubcommand::*;
+ let repo = RepoInfo::get_current(host_name, self.repo.as_deref(), self.remote.as_deref())?;
+ let api = keys.get_api(repo.host_url())?;
+ let repo = repo
+ .name()
+ .ok_or_eyre("couldn't get repo name, try specifying with --repo")?;
+ match self.command {
Create { title, body } => create_issue(&repo, &api, title, body).await?,
View { id, command } => match command.unwrap_or(ViewCommand::Body) {
ViewCommand::Body => view_issue(&repo, &api, id).await?,
@@ -122,7 +135,7 @@ impl IssueCommand {
}
async fn create_issue(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
title: String,
body: Option<String>,
@@ -163,7 +176,7 @@ async fn create_issue(
Ok(())
}
-async fn view_issue(repo: &RepoInfo, api: &Forgejo, id: u64) -> eyre::Result<()> {
+async fn view_issue(repo: &RepoName, api: &Forgejo, id: u64) -> eyre::Result<()> {
let issue = api.issue_get_issue(repo.owner(), repo.name(), id).await?;
let title = issue
.title
@@ -186,7 +199,7 @@ async fn view_issue(repo: &RepoInfo, api: &Forgejo, id: u64) -> eyre::Result<()>
Ok(())
}
async fn view_issues(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
query_str: Option<String>,
labels: Option<String>,
@@ -240,7 +253,7 @@ async fn view_issues(
Ok(())
}
-async fn view_comment(repo: &RepoInfo, api: &Forgejo, id: u64, idx: usize) -> eyre::Result<()> {
+async fn view_comment(repo: &RepoName, api: &Forgejo, id: u64, idx: usize) -> eyre::Result<()> {
let query = IssueGetCommentsQuery {
since: None,
before: None,
@@ -255,7 +268,7 @@ async fn view_comment(repo: &RepoInfo, api: &Forgejo, id: u64, idx: usize) -> ey
Ok(())
}
-async fn view_comments(repo: &RepoInfo, api: &Forgejo, id: u64) -> eyre::Result<()> {
+async fn view_comments(repo: &RepoName, api: &Forgejo, id: u64) -> eyre::Result<()> {
let query = IssueGetCommentsQuery {
since: None,
before: None,
@@ -294,7 +307,7 @@ fn print_comment(comment: &Comment) -> eyre::Result<()> {
Ok(())
}
-async fn browse_issue(repo: &RepoInfo, api: &Forgejo, id: Option<u64>) -> eyre::Result<()> {
+async fn browse_issue(repo: &RepoName, api: &Forgejo, id: Option<u64>) -> eyre::Result<()> {
match id {
Some(id) => {
let issue = api.issue_get_issue(repo.owner(), repo.name(), id).await?;
@@ -317,7 +330,7 @@ async fn browse_issue(repo: &RepoInfo, api: &Forgejo, id: Option<u64>) -> eyre::
}
async fn add_comment(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
issue: u64,
body: Option<String>,
@@ -344,7 +357,7 @@ async fn add_comment(
}
async fn edit_title(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
issue: u64,
new_title: Option<String>,
@@ -390,7 +403,7 @@ async fn edit_title(
}
async fn edit_body(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
issue: u64,
new_body: Option<String>,
@@ -430,7 +443,7 @@ async fn edit_body(
}
async fn edit_comment(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
issue: u64,
idx: usize,
@@ -478,7 +491,7 @@ async fn edit_comment(
}
async fn close_issue(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
issue: u64,
message: Option<Option<String>>,
diff --git a/src/main.rs b/src/main.rs
index db4fe20..e6dc07a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,6 @@
use clap::{Parser, Subcommand};
-use eyre::eyre;
+use eyre::{eyre, Context};
use tokio::io::AsyncWriteExt;
-use url::Url;
mod keys;
use keys::*;
@@ -13,8 +12,8 @@ mod repo;
#[derive(Parser, Debug)]
pub struct App {
- #[clap(long, short = 'R')]
- remote: Option<String>,
+ #[clap(long, short = 'H')]
+ host: Option<String>,
#[clap(subcommand)]
command: Command,
}
@@ -23,15 +22,13 @@ pub struct App {
pub enum Command {
#[clap(subcommand)]
Repo(repo::RepoCommand),
- #[clap(subcommand)]
Issue(issues::IssueCommand),
User {
#[clap(long, short)]
- host: Option<String>,
+ remote: Option<String>,
},
#[clap(subcommand)]
Auth(auth::AuthCommand),
- #[clap(subcommand)]
Release(release::ReleaseCommand),
}
@@ -40,21 +37,21 @@ async fn main() -> eyre::Result<()> {
let args = App::parse();
let mut keys = KeyInfo::load().await?;
- let remote_name = args.remote.as_deref();
+ let host_name = args.host.as_deref();
+ // let remote = repo::RepoInfo::get_current(host_name, remote_name)?;
match args.command {
- Command::Repo(subcommand) => subcommand.run(&keys, remote_name).await?,
- Command::Issue(subcommand) => subcommand.run(&keys, remote_name).await?,
- Command::User { host } => {
- let host = host.map(|host| Url::parse(&host)).transpose()?;
- let url = match host {
- Some(url) => url,
- None => repo::RepoInfo::get_current(remote_name)?.url().clone(),
- };
+ Command::Repo(subcommand) => subcommand.run(&keys, host_name).await?,
+ Command::Issue(subcommand) => subcommand.run(&keys, host_name).await?,
+ Command::User { remote } => {
+ let url = repo::RepoInfo::get_current(host_name, None, remote.as_deref())
+ .wrap_err("could not find host, try specifying with --host")?
+ .host_url()
+ .clone();
let name = keys.get_login(&url)?.username();
eprintln!("currently signed in to {name}@{url}");
}
Command::Auth(subcommand) => subcommand.run(&mut keys).await?,
- Command::Release(subcommand) => subcommand.run(&mut keys, remote_name).await?,
+ Command::Release(subcommand) => subcommand.run(&mut keys, host_name).await?,
}
keys.save().await?;
diff --git a/src/release.rs b/src/release.rs
index 12e4923..c5883e9 100644
--- a/src/release.rs
+++ b/src/release.rs
@@ -1,15 +1,28 @@
-use clap::Subcommand;
-use eyre::{bail, eyre};
+use clap::{Args, Subcommand};
+use eyre::{bail, eyre, OptionExt};
use forgejo_api::{
structs::{RepoCreateReleaseAttachmentQuery, RepoListReleasesQuery},
Forgejo,
};
use tokio::io::AsyncWriteExt;
-use crate::{keys::KeyInfo, repo::RepoInfo};
+use crate::{
+ keys::KeyInfo,
+ repo::{RepoInfo, RepoName},
+};
+
+#[derive(Args, Clone, Debug)]
+pub struct ReleaseCommand {
+ #[clap(long, short = 'R')]
+ remote: Option<String>,
+ #[clap(long, short)]
+ repo: Option<String>,
+ #[clap(subcommand)]
+ command: ReleaseSubcommand,
+}
#[derive(Subcommand, Clone, Debug)]
-pub enum ReleaseCommand {
+pub enum ReleaseSubcommand {
Create {
name: String,
#[clap(long, short = 'T')]
@@ -103,10 +116,14 @@ pub enum AssetCommand {
impl ReleaseCommand {
pub async fn run(self, keys: &KeyInfo, remote_name: Option<&str>) -> eyre::Result<()> {
- let repo = RepoInfo::get_current(remote_name)?;
+ let repo =
+ RepoInfo::get_current(remote_name, self.repo.as_deref(), self.remote.as_deref())?;
let api = keys.get_api(&repo.host_url())?;
- match self {
- Self::Create {
+ let repo = repo
+ .name()
+ .ok_or_eyre("couldn't get repo name, try specifying with --repo")?;
+ match self.command {
+ ReleaseSubcommand::Create {
name,
create_tag,
tag,
@@ -121,7 +138,7 @@ impl ReleaseCommand {
)
.await?
}
- Self::Edit {
+ ReleaseSubcommand::Edit {
name,
rename,
tag,
@@ -129,14 +146,18 @@ impl ReleaseCommand {
draft,
prerelease,
} => edit_release(&repo, &api, name, rename, tag, body, draft, prerelease).await?,
- Self::Delete { name, by_tag } => delete_release(&repo, &api, name, by_tag).await?,
- Self::List {
+ ReleaseSubcommand::Delete { name, by_tag } => {
+ delete_release(&repo, &api, name, by_tag).await?
+ }
+ ReleaseSubcommand::List {
include_prerelease,
include_draft,
} => list_releases(&repo, &api, include_prerelease, include_draft).await?,
- Self::View { name, by_tag } => view_release(&repo, &api, name, by_tag).await?,
- Self::Browse { name } => browse_release(&repo, &api, name).await?,
- Self::Asset(subcommand) => match subcommand {
+ ReleaseSubcommand::View { name, by_tag } => {
+ view_release(&repo, &api, name, by_tag).await?
+ }
+ ReleaseSubcommand::Browse { name } => browse_release(&repo, &api, name).await?,
+ ReleaseSubcommand::Asset(subcommand) => match subcommand {
AssetCommand::Create {
release,
path,
@@ -157,7 +178,7 @@ impl ReleaseCommand {
}
async fn create_release(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
name: String,
create_tag: Option<Option<String>>,
@@ -241,7 +262,7 @@ async fn create_release(
}
async fn edit_release(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
name: String,
rename: Option<String>,
@@ -280,7 +301,7 @@ async fn edit_release(
}
async fn list_releases(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
prerelease: bool,
draft: bool,
@@ -321,7 +342,7 @@ async fn list_releases(
}
async fn view_release(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
name: String,
by_tag: bool,
@@ -384,7 +405,7 @@ async fn view_release(
Ok(())
}
-async fn browse_release(repo: &RepoInfo, api: &Forgejo, name: Option<String>) -> eyre::Result<()> {
+async fn browse_release(repo: &RepoName, api: &Forgejo, name: Option<String>) -> eyre::Result<()> {
match name {
Some(name) => {
let release = find_release(repo, api, &name).await?;
@@ -395,16 +416,20 @@ async fn browse_release(repo: &RepoInfo, api: &Forgejo, name: Option<String>) ->
open::that(html_url.as_str())?;
}
None => {
- let mut url = repo.url().clone();
- url.path_segments_mut().unwrap().push("releases");
- open::that(url.as_str())?;
+ let repo_data = api.repo_get(repo.owner(), repo.name()).await?;
+ let mut html_url = repo_data
+ .html_url
+ .clone()
+ .ok_or_else(|| eyre::eyre!("repository does not have html_url"))?;
+ html_url.path_segments_mut().unwrap().push("releases");
+ open::that(html_url.as_str())?;
}
}
Ok(())
}
async fn create_asset(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
release: String,
file: std::path::PathBuf,
@@ -441,7 +466,7 @@ async fn create_asset(
}
async fn delete_asset(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
release: String,
asset: String,
@@ -467,7 +492,7 @@ async fn delete_asset(
}
async fn download_asset(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
release: String,
asset: String,
@@ -526,7 +551,7 @@ async fn download_asset(
}
async fn find_release(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
name: &str,
) -> eyre::Result<forgejo_api::structs::Release> {
@@ -548,7 +573,7 @@ async fn find_release(
}
async fn delete_release(
- repo: &RepoInfo,
+ repo: &RepoName,
api: &Forgejo,
name: String,
by_tag: bool,
diff --git a/src/repo.rs b/src/repo.rs
index c02cf9d..bc6f5b0 100644
--- a/src/repo.rs
+++ b/src/repo.rs
@@ -1,81 +1,253 @@
use clap::Subcommand;
-use eyre::eyre;
+use eyre::{eyre, OptionExt};
use forgejo_api::structs::CreateRepoOption;
use url::Url;
pub struct RepoInfo {
- owner: String,
- name: String,
url: Url,
+ name: Option<RepoName>,
}
impl RepoInfo {
- pub fn get_current(remote: Option<&str>) -> eyre::Result<Self> {
- let repo = git2::Repository::open(".")?;
- let url = get_remote(&repo, remote)?;
-
- 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 get_current(
+ host: Option<&str>,
+ repo: Option<&str>,
+ remote: Option<&str>,
+ ) -> eyre::Result<Self> {
+ // l = domain/owner/name
+ // s = owner/name
+ // x = is present
+ // i = found locally by git
+ //
+ // | repo | host | remote | ans-host | ans-repo |
+ // |------|------|--------|----------|----------|
+ // | l | x | x | repo | repo |
+ // | l | x | i | repo | repo |
+ // | l | x | | repo | repo |
+ // | l | | x | repo | repo |
+ // | l | | i | repo | repo |
+ // | l | | | repo | repo |
+ // | s | x | x | host | repo |
+ // | s | x | i | host | repo |
+ // | s | x | | host | repo |
+ // | s | | x | remote | repo |
+ // | s | | i | remote | repo |
+ // | s | | | err | repo |
+ // | | x | x | remote | remote |
+ // | | x | i | host | ?remote |
+ // | | x | | host | none |
+ // | | | x | remote | remote |
+ // | | | i | remote | remote |
+ // | | | | err | remote |
+ //
+ // | repo | host | remote | ans-host | ans-repo |
+ // |------|------|--------|----------|----------|
+ // | l | x | x | repo | repo |
+ // | l | x | | repo | repo |
+ // | l | | x | repo | repo |
+ // | l | | | repo | repo |
+ // | s | x | x | host | repo |
+ // | s | x | | host | repo |
+ // | s | | x | remote | repo |
+ // | s | | | err | repo |
+ // | | x | x | remote | remote |
+ // | | x | | remote | remote |
+ // | | | x | remote | remote |
+ // | | | | err | remote |
+
+ // let repo_name;
+ //
+ // let repo_url;
+ // let remote;
+ // let host;
+ //
+ // let url = if repo_url { repo_url }
+ // else if repo_name { host.or(remote) }
+ // else { remote.or_host() }
+ //
+ // let name = repo_name.or(remote)
+
+ let mut repo_url: Option<Url> = None;
+ let mut repo_name: Option<RepoName> = None;
+
+ if let Some(repo) = repo {
+ let (head, name) = repo
+ .rsplit_once("/")
+ .ok_or_eyre("repo name must contain owner and name")?;
+ let name = name.strip_suffix(".git").unwrap_or(name);
+ match head.rsplit_once("/") {
+ Some((url, owner)) => {
+ if let Ok(url) = Url::parse(url) {
+ repo_url = Some(url);
+ } else if let Ok(url) = Url::parse(&format!("https://{url}/")) {
+ repo_url = Some(url);
+ }
+ repo_name = Some(RepoName {
+ owner: owner.to_owned(),
+ name: name.to_owned(),
+ });
+ }
+ None => {
+ repo_name = Some(RepoName {
+ owner: head.to_owned(),
+ name: name.to_owned(),
+ });
+ }
+ }
+ }
+
+ let repo_url = repo_url;
+ let repo_name = repo_name;
+
+ let host_url = host.and_then(|host| {
+ Url::parse(host)
+ .ok()
+ .or_else(|| Url::parse(&format!("https://{host}/")).ok())
+ });
+
+ let (remote_url, remote_repo_name) = {
+ let mut out = (None, None);
+ if let Ok(local_repo) = git2::Repository::open(".") {
+ let tmp;
+ let mut name = remote;
+
+ // if the user didn't specify a remote, try guessing other ways
+ let mut tmp2;
+ if name.is_none() {
+ let all_remotes = local_repo.remotes()?;
+ // if there's only one remote, use that
+ if all_remotes.len() == 1 {
+ if let Some(remote_name) = all_remotes.get(0) {
+ tmp2 = Some(remote_name.to_owned());
+ name = tmp2.as_deref();
+ }
+ // if there's a remote whose host url matches the one
+ // specified with `--host`, use that
+ //
+ // This is different than using `--host` itself, since this
+ // will include the repo name, which `--host` can't do.
+ } else if let Some(host_url) = &host_url {
+ for remote_name in all_remotes.iter() {
+ let Some(remote_name) = remote_name else {
+ continue;
+ };
+ let remote = local_repo.find_remote(remote_name)?;
+
+ if let Some(url) = remote.url() {
+ let (url, _) = url_strip_repo_name(Url::parse(url)?)?;
+ if url.host_str() == host_url.host_str()
+ && url.path() == host_url.path()
+ {
+ tmp2 = Some(remote_name.to_owned());
+ name = tmp2.as_deref();
+ }
+ }
+ }
+ }
+ }
+
+ // if there isn't an obvious answer, guess from the current
+ // branch's tracking remote
+ if name.is_none() {
+ let head = local_repo.head()?;
+ let branch_name = head.name().ok_or_else(|| eyre!("branch name not UTF-8"))?;
+ tmp = local_repo.branch_upstream_remote(branch_name).ok();
+ name = tmp
+ .as_ref()
+ .map(|remote| {
+ remote
+ .as_str()
+ .ok_or_else(|| eyre!("remote name not UTF-8"))
+ })
+ .transpose()?;
+ }
+
+ if let Some(name) = name {
+ if let Ok(remote) = local_repo.find_remote(name) {
+ let url_s = std::str::from_utf8(remote.url_bytes())?;
+ let url = Url::parse(url_s)?;
+ let (url, name) = url_strip_repo_name(url)?;
+
+ out = (Some(url), Some(name))
+ }
+ }
+ } else {
+ eyre::ensure!(remote.is_none(), "remote specified but no git repo found");
+ }
+ out
+ };
+
+ let (url, name) = if repo_url.is_some() {
+ (repo_url, repo_name)
+ } else if repo_name.is_some() {
+ (host_url.or(remote_url), repo_name)
+ } else {
+ if remote.is_some() {
+ (remote_url, remote_repo_name)
+ } else if host_url.is_none() || remote_url == host_url {
+ (remote_url, remote_repo_name)
+ } else {
+ (host_url, None)
+ }
+ };
+
+ let info = match (url, name) {
+ (Some(url), name) => RepoInfo { url, name },
+ (None, Some(_)) => eyre::bail!("cannot find repo, no host specified"),
+ (None, None) => eyre::bail!("no repo info specified"),
+ };
+
+ Ok(info)
}
- pub fn name(&self) -> &str {
- &self.name
+ pub fn name(&self) -> Option<&RepoName> {
+ self.name.as_ref()
}
- pub fn url(&self) -> &Url {
+ pub fn host_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 url_strip_repo_name(mut url: Url) -> eyre::Result<(Url, RepoName)> {
+ let mut iter = url
+ .path_segments()
+ .ok_or_eyre("repo url cannot be a base")?
+ .rev();
+
+ let name = iter.next().ok_or_eyre("repo url too short")?;
+ let name = name.strip_suffix(".git").unwrap_or(name).to_owned();
+
+ let owner = iter.next().ok_or_eyre("repo url too short")?.to_owned();
+
+ // Remove the username and repo name from the url
+ url.path_segments_mut()
+ .map_err(|_| eyre!("repo url cannot be a base"))?
+ .pop()
+ .pop();
+
+ Ok((url, RepoName { owner, name }))
}
-fn get_remote(repo: &git2::Repository, name: Option<&str>) -> eyre::Result<Url> {
- if let Some(name) = name {
- if let Ok(url) = Url::parse(name) {
- return Ok(url);
- }
+#[derive(Debug)]
+pub struct RepoName {
+ owner: String,
+ name: String,
+}
+
+impl RepoName {
+ pub fn owner(&self) -> &str {
+ &self.owner
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
}
- let remote_name;
- let remote_name = match name {
- Some(name) => name,
- None => {
- let head = repo.head()?;
- let branch_name = head.name().ok_or_else(|| eyre!("branch name not UTF-8"))?;
- remote_name = repo.branch_upstream_remote(branch_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 {
- host: String,
repo: String,
// flags
@@ -91,15 +263,22 @@ pub enum RepoCommand {
#[clap(long, short)]
push: bool,
},
- Info,
- Browse,
+ Info {
+ name: Option<String>,
+ #[clap(long, short = 'R')]
+ remote: Option<String>,
+ },
+ Browse {
+ name: Option<String>,
+ #[clap(long, short = 'R')]
+ remote: Option<String>,
+ },
}
impl RepoCommand {
- pub async fn run(self, keys: &crate::KeyInfo, remote_name: Option<&str>) -> eyre::Result<()> {
+ pub async fn run(self, keys: &crate::KeyInfo, host_name: Option<&str>) -> eyre::Result<()> {
match self {
RepoCommand::Create {
- host,
repo,
description,
@@ -107,8 +286,8 @@ impl RepoCommand {
set_upstream,
push,
} => {
- let host = Url::parse(&host)?;
- let api = keys.get_api(&host)?;
+ let host = RepoInfo::get_current(host_name, None, None)?;
+ let api = keys.get_api(host.host_url())?;
let repo_spec = CreateRepoOption {
auto_init: Some(false),
default_branch: Some("main".into()),
@@ -127,7 +306,7 @@ impl RepoCommand {
.full_name
.as_ref()
.ok_or_else(|| eyre::eyre!("new_repo does not have full_name"))?;
- eprintln!("created new repo at {}", host.join(&full_name)?);
+ eprintln!("created new repo at {}", host.host_url().join(&full_name)?);
if set_upstream.is_some() || push {
let repo = git2::Repository::open(".")?;
@@ -158,22 +337,26 @@ impl RepoCommand {
}
}
}
- RepoCommand::Info => {
- let repo = RepoInfo::get_current(remote_name)?;
+ RepoCommand::Info { name, remote } => {
+ let repo = RepoInfo::get_current(host_name, name.as_deref(), remote.as_deref())?;
let api = keys.get_api(&repo.host_url())?;
+ let repo = repo
+ .name()
+ .ok_or_eyre("couldn't get repo name, please specify")?;
let repo = api.repo_get(repo.owner(), repo.name()).await?;
+
dbg!(repo);
}
- RepoCommand::Browse => {
- let repo = RepoInfo::get_current(remote_name)?;
+ RepoCommand::Browse { name, remote } => {
+ let repo = RepoInfo::get_current(host_name, name.as_deref(), remote.as_deref())?;
let mut url = repo.host_url().clone();
- let new_path = format!(
- "{}/{}/{}",
- url.path().strip_suffix("/").unwrap_or(url.path()),
- repo.owner(),
- repo.name(),
- );
- url.set_path(&new_path);
+ let repo = repo
+ .name()
+ .ok_or_eyre("couldn't get repo name, please specify")?;
+ url.path_segments_mut()
+ .map_err(|_| eyre!("url invalid"))?
+ .extend([repo.owner(), repo.name()]);
+
open::that(url.as_str())?;
}
};