diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..b9f4e49 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,59 @@ +use clap::Subcommand; + +#[derive(Subcommand, Clone, Debug)] +pub enum AuthCommand { + Login, + Logout { + host: String, + }, + AddKey { + /// The domain name of the forgejo instance. + host: String, + /// The user that the key is associated with + user: String, + /// The key to add. If not present, the key will be read in from stdin. + key: Option, + }, + List, +} + +impl AuthCommand { + pub async fn run(self, keys: &mut crate::KeyInfo) -> eyre::Result<()> { + match self { + AuthCommand::Login => { + todo!(); + // let user = readline("username: ").await?; + // let pass = readline("password: ").await?; + } + AuthCommand::Logout { host } => { + let info_opt = keys.hosts.remove(&host); + if let Some(info) = info_opt { + eprintln!("signed out of {}@{}", &info.username(), host); + } else { + eprintln!("already not signed in to {host}"); + } + } + AuthCommand::AddKey { host, user, key } => { + let key = match key { + Some(key) => key, + None => crate::readline("new key: ").await?, + }; + if keys.hosts.get(&user).is_none() { + keys.hosts.insert(host, crate::keys::LoginInfo::new(user, key)); + } else { + println!("key for {} already exists", host); + } + } + AuthCommand::List => { + if keys.hosts.is_empty() { + println!("No logins."); + } + for (host_url, login_info) in &keys.hosts { + println!("{}@{}", login_info.username(), host_url); + } + } + } + Ok(()) + } +} + diff --git a/src/main.rs b/src/main.rs index 7f6ea6f..52cfa4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,9 @@ use url::Url; mod keys; use keys::*; +mod repo; +mod auth; + #[derive(Parser, Debug)] pub struct App { #[clap(subcommand)] @@ -18,53 +21,13 @@ pub struct App { #[derive(Subcommand, Clone, Debug)] pub enum Command { #[clap(subcommand)] - Repo(RepoCommand), + Repo(repo::RepoCommand), User { #[clap(long, short)] host: Option, }, #[clap(subcommand)] - Auth(AuthCommand), -} - -#[derive(Subcommand, Clone, Debug)] -pub enum RepoCommand { - Create { - host: String, - repo: String, - - // flags - #[clap(long, short)] - description: Option, - #[clap(long, short)] - private: bool, - /// Sets the new repo to be the `origin` remote of the current local repo. - #[clap(long, short)] - set_upstream: Option, - /// Pushes the current branch to the default branch on the new repo. - /// Implies `--set-upstream=origin` (setting upstream manual overrides this) - #[clap(long, short)] - push: bool, - }, - Info, - Browse, -} - -#[derive(Subcommand, Clone, Debug)] -pub enum AuthCommand { - Login, - Logout { - host: String, - }, - AddKey { - /// The domain name of the forgejo instance. - host: String, - /// The user that the key is associated with - user: String, - /// The key to add. If not present, the key will be read in from stdin. - key: Option, - }, - List, + Auth(auth::AuthCommand), } #[tokio::main] @@ -73,75 +36,7 @@ async fn main() -> eyre::Result<()> { let mut keys = KeyInfo::load().await?; match args.command { - Command::Repo(repo_subcommand) => match repo_subcommand { - RepoCommand::Create { - host, - repo, - - description, - private, - set_upstream, - push, - } => { - let host = Url::parse(&host)?; - let login = keys.get_login(&host)?; - let api = login.api_for(&host)?; - let repo_spec = CreateRepoOption { - auto_init: false, - default_branch: "main".into(), - description, - gitignores: String::new(), - issue_labels: String::new(), - license: String::new(), - name: repo.clone(), - private, - readme: String::new(), - template: false, - trust_model: forgejo_api::TrustModel::Default, - }; - let new_repo = api.create_repo(repo_spec).await?; - eprintln!( - "created new repo at {}", - host.join(&format!("{}/{}", login.username(), repo))? - ); - - let upstream = set_upstream.as_deref().unwrap_or("origin"); - - let repo = git2::Repository::open(".")?; - let mut remote = if set_upstream.is_some() || push { - repo.remote(upstream, new_repo.clone_url.as_str())? - } else { - repo.find_remote(upstream)? - }; - - if push { - remote.push::<&str>(&[], None)?; - } - } - RepoCommand::Info => { - let (host, repo) = keys.get_current()?; - let api = host.api()?; - let repo = api.get_repo(repo.owner(), repo.name()).await?; - match repo { - Some(repo) => { - dbg!(repo); - } - None => eprintln!("repo not found"), - } - } - RepoCommand::Browse => { - let (host, repo) = keys.get_current()?; - let mut url = host.url().clone(); - let new_path = format!( - "{}/{}/{}", - url.path().strip_suffix("/").unwrap_or(url.path()), - repo.owner(), - repo.name(), - ); - url.set_path(&new_path); - open::that(url.as_str())?; - } - }, + 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 { @@ -153,40 +48,7 @@ async fn main() -> eyre::Result<()> { }; eprintln!("currently signed in to {name}@{url}"); } - Command::Auth(auth_subcommand) => match auth_subcommand { - AuthCommand::Login => { - todo!(); - // let user = readline("username: ").await?; - // let pass = readline("password: ").await?; - } - AuthCommand::Logout { host } => { - let info_opt = keys.hosts.remove(&host); - if let Some(info) = info_opt { - eprintln!("signed out of {}@{}", &info.username(), host); - } else { - eprintln!("already not signed in to {host}"); - } - } - AuthCommand::AddKey { host, user, key } => { - let key = match key { - Some(key) => key, - None => readline("new key: ").await?, - }; - if keys.hosts.get(&user).is_none() { - keys.hosts.insert(host, LoginInfo::new(user, key)); - } else { - println!("key for {} already exists", host); - } - } - AuthCommand::List => { - if keys.hosts.is_empty() { - println!("No logins."); - } - for (host_url, login_info) in &keys.hosts { - println!("{}@{}", login_info.username(), host_url); - } - } - }, + Command::Auth(auth_subcommand) => auth_subcommand.run(&mut keys).await?, } keys.save().await?; diff --git a/src/repo.rs b/src/repo.rs new file mode 100644 index 0000000..863987b --- /dev/null +++ b/src/repo.rs @@ -0,0 +1,101 @@ +use clap::Subcommand; +use url::Url; +use forgejo_api::CreateRepoOption; + +#[derive(Subcommand, Clone, Debug)] +pub enum RepoCommand { + Create { + host: String, + repo: String, + + // flags + #[clap(long, short)] + description: Option, + #[clap(long, short)] + private: bool, + /// Sets the new repo to be the `origin` remote of the current local repo. + #[clap(long, short)] + set_upstream: Option, + /// Pushes the current branch to the default branch on the new repo. + /// Implies `--set-upstream=origin` (setting upstream manual overrides this) + #[clap(long, short)] + push: bool, + }, + Info, + Browse, +} + +impl RepoCommand { + pub async fn run(self, keys: &crate::KeyInfo) -> eyre::Result<()> { + match self { + RepoCommand::Create { + host, + repo, + + description, + private, + set_upstream, + push, + } => { + let host = Url::parse(&host)?; + let login = keys.get_login(&host)?; + let api = login.api_for(&host)?; + let repo_spec = CreateRepoOption { + auto_init: false, + default_branch: "main".into(), + description, + gitignores: String::new(), + issue_labels: String::new(), + license: String::new(), + name: repo.clone(), + private, + readme: String::new(), + template: false, + trust_model: forgejo_api::TrustModel::Default, + }; + let new_repo = api.create_repo(repo_spec).await?; + eprintln!( + "created new repo at {}", + host.join(&format!("{}/{}", login.username(), repo))? + ); + + let upstream = set_upstream.as_deref().unwrap_or("origin"); + + let repo = git2::Repository::open(".")?; + let mut remote = if set_upstream.is_some() || push { + repo.remote(upstream, new_repo.clone_url.as_str())? + } else { + repo.find_remote(upstream)? + }; + + if push { + remote.push::<&str>(&[], None)?; + } + } + RepoCommand::Info => { + let (host, repo) = keys.get_current()?; + let api = host.api()?; + let repo = api.get_repo(repo.owner(), repo.name()).await?; + match repo { + Some(repo) => { + dbg!(repo); + } + None => eprintln!("repo not found"), + } + } + RepoCommand::Browse => { + let (host, repo) = keys.get_current()?; + let mut url = host.url().clone(); + let new_path = format!( + "{}/{}/{}", + url.path().strip_suffix("/").unwrap_or(url.path()), + repo.owner(), + repo.name(), + ); + url.set_path(&new_path); + open::that(url.as_str())?; + } + }; + Ok(()) + } +}