Replace calls to git with git2 crate (#5)
				
					
				
			This commit is contained in:
		
							parent
							
								
									2c30803476
								
							
						
					
					
						commit
						c1806f5aa6
					
				
					 4 changed files with 180 additions and 98 deletions
				
			
		
							
								
								
									
										68
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										68
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -122,6 +122,9 @@ name = "cc"
 | 
			
		|||
version = "1.0.79"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "jobserver",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cfg-if"
 | 
			
		||||
| 
						 | 
				
			
			@ -271,6 +274,7 @@ dependencies = [
 | 
			
		|||
 "eyre",
 | 
			
		||||
 "forgejo-api",
 | 
			
		||||
 "futures",
 | 
			
		||||
 "git2",
 | 
			
		||||
 "open",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
| 
						 | 
				
			
			@ -428,6 +432,21 @@ version = "0.27.3"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "git2"
 | 
			
		||||
version = "0.17.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bitflags 1.3.2",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "libgit2-sys",
 | 
			
		||||
 "log",
 | 
			
		||||
 "openssl-probe",
 | 
			
		||||
 "openssl-sys",
 | 
			
		||||
 "url",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "h2"
 | 
			
		||||
version = "0.3.20"
 | 
			
		||||
| 
						 | 
				
			
			@ -624,6 +643,15 @@ version = "1.0.8"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "jobserver"
 | 
			
		||||
version = "0.1.26"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "js-sys"
 | 
			
		||||
version = "0.3.64"
 | 
			
		||||
| 
						 | 
				
			
			@ -645,6 +673,46 @@ version = "0.2.147"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libgit2-sys"
 | 
			
		||||
version = "0.15.2+1.6.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "libssh2-sys",
 | 
			
		||||
 "libz-sys",
 | 
			
		||||
 "openssl-sys",
 | 
			
		||||
 "pkg-config",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libssh2-sys"
 | 
			
		||||
version = "0.3.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "libz-sys",
 | 
			
		||||
 "openssl-sys",
 | 
			
		||||
 "pkg-config",
 | 
			
		||||
 "vcpkg",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libz-sys"
 | 
			
		||||
version = "1.1.9"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "pkg-config",
 | 
			
		||||
 "vcpkg",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "linux-raw-sys"
 | 
			
		||||
version = "0.3.8"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ directories = "5.0.1"
 | 
			
		|||
eyre = "0.6.8"
 | 
			
		||||
forgejo-api = { path = "./forgejo-api" }
 | 
			
		||||
futures = "0.3.28"
 | 
			
		||||
git2 = "0.17.2"
 | 
			
		||||
open = "5.0.0"
 | 
			
		||||
serde = { version = "1.0.170", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.100"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
use reqwest::{Client, Request, StatusCode};
 | 
			
		||||
use serde::{de::DeserializeOwned, Serialize};
 | 
			
		||||
use url::Url;
 | 
			
		||||
use soft_assert::*;
 | 
			
		||||
use reqwest::{Client, StatusCode, Request};
 | 
			
		||||
use url::Url;
 | 
			
		||||
 | 
			
		||||
pub struct Forgejo {
 | 
			
		||||
    url: Url,
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ pub enum ForgejoError {
 | 
			
		|||
    #[error("unexpected status code {} {}", .0.as_u16(), .0.canonical_reason().unwrap_or(""))]
 | 
			
		||||
    UnexpectedStatusCode(StatusCode),
 | 
			
		||||
    #[error("{} {}: {}", .0.as_u16(), .0.canonical_reason().unwrap_or(""), .1)]
 | 
			
		||||
    ApiError(StatusCode, String)
 | 
			
		||||
    ApiError(StatusCode, String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<reqwest::Error> for ForgejoError {
 | 
			
		||||
| 
						 | 
				
			
			@ -37,23 +37,32 @@ impl From<reqwest::Error> for ForgejoError {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl Forgejo {
 | 
			
		||||
    pub fn new(api_key: &str, url: Url) -> Result<Self, ForgejoError> { 
 | 
			
		||||
    pub fn new(api_key: &str, url: Url) -> Result<Self, ForgejoError> {
 | 
			
		||||
        Self::with_user_agent(api_key, url, "forgejo-api-rs")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn with_user_agent(api_key: &str, url: Url, user_agent: &str) -> Result<Self, ForgejoError> {
 | 
			
		||||
        soft_assert!(matches!(url.scheme(), "http" | "https"), Err(ForgejoError::HttpRequired));
 | 
			
		||||
    pub fn with_user_agent(
 | 
			
		||||
        api_key: &str,
 | 
			
		||||
        url: Url,
 | 
			
		||||
        user_agent: &str,
 | 
			
		||||
    ) -> Result<Self, ForgejoError> {
 | 
			
		||||
        soft_assert!(
 | 
			
		||||
            matches!(url.scheme(), "http" | "https"),
 | 
			
		||||
            Err(ForgejoError::HttpRequired)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let mut headers = reqwest::header::HeaderMap::new();
 | 
			
		||||
        let mut key_header: reqwest::header::HeaderValue = format!("token {api_key}").try_into().map_err(|_| ForgejoError::KeyNotAscii)?;
 | 
			
		||||
        let mut key_header: reqwest::header::HeaderValue = format!("token {api_key}")
 | 
			
		||||
            .try_into()
 | 
			
		||||
            .map_err(|_| ForgejoError::KeyNotAscii)?;
 | 
			
		||||
        // key_header.set_sensitive(true);
 | 
			
		||||
        headers.insert("Authorization", key_header);
 | 
			
		||||
        let client = Client::builder().user_agent(user_agent).default_headers(headers).build()?;
 | 
			
		||||
        let client = Client::builder()
 | 
			
		||||
            .user_agent(user_agent)
 | 
			
		||||
            .default_headers(headers)
 | 
			
		||||
            .build()?;
 | 
			
		||||
        dbg!(&client);
 | 
			
		||||
        Ok(Self { 
 | 
			
		||||
            url,
 | 
			
		||||
            client,
 | 
			
		||||
        })
 | 
			
		||||
        Ok(Self { url, client })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_repo(&self, user: &str, repo: &str) -> Result<Option<Repo>, ForgejoError> {
 | 
			
		||||
| 
						 | 
				
			
			@ -93,29 +102,42 @@ impl Forgejo {
 | 
			
		|||
        self.execute_opt(request).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn post<T: Serialize, U: DeserializeOwned>(&self, path: &str, body: &T) -> Result<U, ForgejoError> {
 | 
			
		||||
    async fn post<T: Serialize, U: DeserializeOwned>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: &str,
 | 
			
		||||
        body: &T,
 | 
			
		||||
    ) -> Result<U, ForgejoError> {
 | 
			
		||||
        let url = self.url.join("api/v1/").unwrap().join(path).unwrap();
 | 
			
		||||
        let request = self.client.post(url).json(body).build()?;
 | 
			
		||||
        self.execute(request).await
 | 
			
		||||
    } 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn execute<T: DeserializeOwned>(&self, request: Request) -> Result<T, ForgejoError> {
 | 
			
		||||
        let response = self.client.execute(dbg!(request)).await?;
 | 
			
		||||
        match response.status() {
 | 
			
		||||
            status if status.is_success() => Ok(response.json::<T>().await?),
 | 
			
		||||
            status if status.is_client_error() => Err(ForgejoError::ApiError(status, response.json::<ErrorMessage>().await?.message)),
 | 
			
		||||
            status => Err(ForgejoError::UnexpectedStatusCode(status))
 | 
			
		||||
            status if status.is_client_error() => Err(ForgejoError::ApiError(
 | 
			
		||||
                status,
 | 
			
		||||
                response.json::<ErrorMessage>().await?.message,
 | 
			
		||||
            )),
 | 
			
		||||
            status => Err(ForgejoError::UnexpectedStatusCode(status)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Like `execute`, but returns `Ok(None)` on 404.
 | 
			
		||||
    async fn execute_opt<T: DeserializeOwned>(&self, request: Request) -> Result<Option<T>, ForgejoError> {
 | 
			
		||||
    async fn execute_opt<T: DeserializeOwned>(
 | 
			
		||||
        &self,
 | 
			
		||||
        request: Request,
 | 
			
		||||
    ) -> Result<Option<T>, ForgejoError> {
 | 
			
		||||
        let response = self.client.execute(dbg!(request)).await?;
 | 
			
		||||
        match response.status() {
 | 
			
		||||
            status if status.is_success() => Ok(Some(response.json::<T>().await?)),
 | 
			
		||||
            StatusCode::NOT_FOUND => Ok(None),
 | 
			
		||||
            status if status.is_client_error() => Err(ForgejoError::ApiError(status, response.json::<ErrorMessage>().await?.message)),
 | 
			
		||||
            status => Err(ForgejoError::UnexpectedStatusCode(status))
 | 
			
		||||
            status if status.is_client_error() => Err(ForgejoError::ApiError(
 | 
			
		||||
                status,
 | 
			
		||||
                response.json::<ErrorMessage>().await?.message,
 | 
			
		||||
            )),
 | 
			
		||||
            status => Err(ForgejoError::UnexpectedStatusCode(status)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -124,14 +146,13 @@ impl Forgejo {
 | 
			
		|||
struct ErrorMessage {
 | 
			
		||||
    message: String,
 | 
			
		||||
    // intentionally ignored, no need for now
 | 
			
		||||
    // url: Url 
 | 
			
		||||
    // url: Url
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize, Debug, PartialEq)]
 | 
			
		||||
pub struct Repo {
 | 
			
		||||
    pub clone_url: Url,
 | 
			
		||||
    #[serde(with="time::serde::rfc3339")]
 | 
			
		||||
    #[serde(with = "time::serde::rfc3339")]
 | 
			
		||||
    pub created_at: time::OffsetDateTime,
 | 
			
		||||
    pub default_branch: String,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +167,7 @@ pub struct Repo {
 | 
			
		|||
pub struct User {
 | 
			
		||||
    pub active: bool,
 | 
			
		||||
    pub avatar_url: Url,
 | 
			
		||||
    #[serde(with="time::serde::rfc3339")]
 | 
			
		||||
    #[serde(with = "time::serde::rfc3339")]
 | 
			
		||||
    pub created: time::OffsetDateTime,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    pub email: String,
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +177,7 @@ pub struct User {
 | 
			
		|||
    pub id: u64,
 | 
			
		||||
    pub is_admin: bool,
 | 
			
		||||
    pub language: String,
 | 
			
		||||
    #[serde(with="time::serde::rfc3339")]
 | 
			
		||||
    #[serde(with = "time::serde::rfc3339")]
 | 
			
		||||
    pub last_login: time::OffsetDateTime,
 | 
			
		||||
    pub location: String,
 | 
			
		||||
    pub login: String,
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +210,7 @@ pub struct CreateRepoOption {
 | 
			
		|||
    pub private: bool,
 | 
			
		||||
    pub readme: String,
 | 
			
		||||
    pub template: bool,
 | 
			
		||||
    pub trust_model: TrustModel
 | 
			
		||||
    pub trust_model: TrustModel,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, Debug, PartialEq)]
 | 
			
		||||
| 
						 | 
				
			
			@ -199,4 +220,4 @@ pub enum TrustModel {
 | 
			
		|||
    Committer,
 | 
			
		||||
    #[serde(rename = "collaboratorcommiter")]
 | 
			
		||||
    CollaboratorCommitter,
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										136
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										136
									
								
								src/main.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -26,8 +26,8 @@ pub enum Command {
 | 
			
		|||
 | 
			
		||||
#[derive(Subcommand, Clone, Debug)]
 | 
			
		||||
pub enum RepoCommand {
 | 
			
		||||
    Create { 
 | 
			
		||||
        host: String, 
 | 
			
		||||
    Create {
 | 
			
		||||
        host: String,
 | 
			
		||||
        repo: String,
 | 
			
		||||
 | 
			
		||||
        // flags
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ pub enum RepoCommand {
 | 
			
		|||
        /// 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
 | 
			
		||||
        push: bool,
 | 
			
		||||
    },
 | 
			
		||||
    Info,
 | 
			
		||||
    Browse,
 | 
			
		||||
| 
						 | 
				
			
			@ -81,9 +81,9 @@ async fn main() -> eyre::Result<()> {
 | 
			
		|||
 | 
			
		||||
    match args.command {
 | 
			
		||||
        Command::Repo(repo_subcommand) => match repo_subcommand {
 | 
			
		||||
            RepoCommand::Create { 
 | 
			
		||||
                host, 
 | 
			
		||||
                repo ,
 | 
			
		||||
            RepoCommand::Create {
 | 
			
		||||
                host,
 | 
			
		||||
                repo,
 | 
			
		||||
 | 
			
		||||
                description,
 | 
			
		||||
                private,
 | 
			
		||||
| 
						 | 
				
			
			@ -91,10 +91,12 @@ async fn main() -> eyre::Result<()> {
 | 
			
		|||
                push,
 | 
			
		||||
            } => {
 | 
			
		||||
                // let (host_domain, host_keys, repo) = keys.get_current_host_and_repo().await?;
 | 
			
		||||
                let host_info = keys.hosts.get(&host).ok_or_else(|| eyre!("not a known host"))?;
 | 
			
		||||
                let host_info = keys
 | 
			
		||||
                    .hosts
 | 
			
		||||
                    .get(&host)
 | 
			
		||||
                    .ok_or_else(|| eyre!("not a known host"))?;
 | 
			
		||||
                let (_, user) = host_info.get_current_user()?;
 | 
			
		||||
                let url = Url::parse(&format!("http://{host}/"))?;
 | 
			
		||||
                let api = Forgejo::new(&user.key, url.clone())?;
 | 
			
		||||
                let api = Forgejo::new(&user.key, host_info.url.clone())?;
 | 
			
		||||
                let repo_spec = CreateRepoOption {
 | 
			
		||||
                    auto_init: false,
 | 
			
		||||
                    default_branch: "main".into(),
 | 
			
		||||
| 
						 | 
				
			
			@ -109,41 +111,28 @@ async fn main() -> eyre::Result<()> {
 | 
			
		|||
                    trust_model: forgejo_api::TrustModel::Default,
 | 
			
		||||
                };
 | 
			
		||||
                let new_repo = api.create_repo(repo_spec).await?;
 | 
			
		||||
                eprintln!("created new repo at {}", url.join(&format!("{}/{}", user.name, repo))?);
 | 
			
		||||
                eprintln!(
 | 
			
		||||
                    "created new repo at {}",
 | 
			
		||||
                    host_info.url.join(&format!("{}/{}", user.name, repo))?
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                let upstream = set_upstream.as_deref().unwrap_or("origin");
 | 
			
		||||
 | 
			
		||||
                if set_upstream.is_some() || push {
 | 
			
		||||
                    let status = tokio::process::Command::new("git")
 | 
			
		||||
                        .arg("remote")
 | 
			
		||||
                        .arg("add")
 | 
			
		||||
                        .arg(upstream)
 | 
			
		||||
                        .arg(new_repo.clone_url.as_str())
 | 
			
		||||
                        .status()
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    if !status.success() {
 | 
			
		||||
                        eprintln!("origin set failed");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                let repo = git2::Repository::open(".")?;
 | 
			
		||||
                let remote = if set_upstream.is_some() || push {
 | 
			
		||||
                    repo.remote(upstream, new_repo.clone_url.as_str())?;
 | 
			
		||||
                } else {
 | 
			
		||||
                    repo.find_remote(upstream)?;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if push {
 | 
			
		||||
                    let status = tokio::process::Command::new("git")
 | 
			
		||||
                        .arg("push")
 | 
			
		||||
                        .arg("-u")
 | 
			
		||||
                        .arg(upstream)
 | 
			
		||||
                        .arg("main")
 | 
			
		||||
                        .status()
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    if !status.success() {
 | 
			
		||||
                        eprintln!("push failed");
 | 
			
		||||
                    }
 | 
			
		||||
                    remote.push(upstream)?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            RepoCommand::Info => {
 | 
			
		||||
                let (host_domain, host_keys, repo) = keys.get_current_host_and_repo().await?;
 | 
			
		||||
                let (_, host_keys, repo) = keys.get_current_host_and_repo().await?;
 | 
			
		||||
                let (_, user) = host_keys.get_current_user()?;
 | 
			
		||||
                let url = Url::parse(&format!("http://{host_domain}/"))?;
 | 
			
		||||
                let api = Forgejo::new(&user.key, url)?;
 | 
			
		||||
                let api = Forgejo::new(&user.key, host_keys.url.clone())?;
 | 
			
		||||
                let repo = api.get_repo(&user.name, &repo).await?;
 | 
			
		||||
                match repo {
 | 
			
		||||
                    Some(repo) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -153,19 +142,27 @@ async fn main() -> eyre::Result<()> {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
            RepoCommand::Browse => {
 | 
			
		||||
                let (host_domain, host_keys, repo) = keys.get_current_host_and_repo().await?;
 | 
			
		||||
                let (_, host_keys, repo) = keys.get_current_host_and_repo().await?;
 | 
			
		||||
                let (_, user) = host_keys.get_current_user()?;
 | 
			
		||||
                open::that(format!("http://{host_domain}/{}/{repo}", user.name))?;
 | 
			
		||||
                open::that(
 | 
			
		||||
                    host_keys
 | 
			
		||||
                        .url
 | 
			
		||||
                        .join(&format!("/{}/{repo}", user.name))?
 | 
			
		||||
                        .as_str(),
 | 
			
		||||
                )?;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Command::User { host } => {
 | 
			
		||||
            let (host_domain, host_keys) = match host.as_deref() {
 | 
			
		||||
                Some(s) => (s, keys.hosts.get(s).ok_or_else(|| eyre!("not a known host"))?),
 | 
			
		||||
            let (_, host_keys) = match host.as_deref() {
 | 
			
		||||
                Some(s) => (
 | 
			
		||||
                    s,
 | 
			
		||||
                    keys.hosts.get(s).ok_or_else(|| eyre!("not a known host"))?,
 | 
			
		||||
                ),
 | 
			
		||||
                None => keys.get_current_host().await?,
 | 
			
		||||
            };
 | 
			
		||||
            let (_, info) = host_keys.get_current_user()?;
 | 
			
		||||
            eprintln!("currently signed in to {}@{}", info.name, host_domain);
 | 
			
		||||
        },
 | 
			
		||||
            eprintln!("currently signed in to {}@{}", info.name, host_keys.url);
 | 
			
		||||
        }
 | 
			
		||||
        Command::Auth(auth_subcommand) => match auth_subcommand {
 | 
			
		||||
            AuthCommand::Login => {
 | 
			
		||||
                todo!();
 | 
			
		||||
| 
						 | 
				
			
			@ -206,7 +203,10 @@ async fn main() -> eyre::Result<()> {
 | 
			
		|||
                name,
 | 
			
		||||
                key,
 | 
			
		||||
            } => {
 | 
			
		||||
                let host_keys = keys.hosts.entry(host.clone()).or_default();
 | 
			
		||||
                let host_keys = keys
 | 
			
		||||
                    .hosts
 | 
			
		||||
                    .get_mut(&host)
 | 
			
		||||
                    .ok_or_else(|| eyre!("unknown host {host}"))?;
 | 
			
		||||
                let key = match key {
 | 
			
		||||
                    Some(key) => key,
 | 
			
		||||
                    None => readline("new key: ").await?,
 | 
			
		||||
| 
						 | 
				
			
			@ -254,30 +254,16 @@ async fn readline(msg: &str) -> eyre::Result<String> {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
async fn get_remotes() -> eyre::Result<Vec<(String, Url)>> {
 | 
			
		||||
    let remotes = String::from_utf8(
 | 
			
		||||
        tokio::process::Command::new("git")
 | 
			
		||||
            .arg("remote")
 | 
			
		||||
            .output()
 | 
			
		||||
            .await?
 | 
			
		||||
            .stdout,
 | 
			
		||||
    )?;
 | 
			
		||||
    let remotes = futures::future::try_join_all(remotes.lines().map(|name| async {
 | 
			
		||||
        let name = name.trim();
 | 
			
		||||
        let url = Url::parse(
 | 
			
		||||
            String::from_utf8(
 | 
			
		||||
                tokio::process::Command::new("git")
 | 
			
		||||
                    .arg("remote")
 | 
			
		||||
                    .arg("get-url")
 | 
			
		||||
                    .arg(name)
 | 
			
		||||
                    .output()
 | 
			
		||||
                    .await?
 | 
			
		||||
                    .stdout,
 | 
			
		||||
            )?
 | 
			
		||||
            .trim(),
 | 
			
		||||
        )?;
 | 
			
		||||
        Ok::<_, eyre::Report>((name.to_string(), url))
 | 
			
		||||
    }))
 | 
			
		||||
    .await?;
 | 
			
		||||
    let repo = git2::Repository::open(".")?;
 | 
			
		||||
    let remotes = repo
 | 
			
		||||
        .remotes()?
 | 
			
		||||
        .iter()
 | 
			
		||||
        .filter_map(|name| {
 | 
			
		||||
            let name = name?.to_string();
 | 
			
		||||
            let url = Url::parse(repo.find_remote(&name).ok()?.url()?).ok()?;
 | 
			
		||||
            Some((name, url))
 | 
			
		||||
        })
 | 
			
		||||
        .collect::<Vec<_>>();
 | 
			
		||||
    Ok(remotes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -295,9 +281,14 @@ async fn get_remote(remotes: &[(String, Url)]) -> eyre::Result<Url> {
 | 
			
		|||
#[derive(serde::Serialize, serde::Deserialize, Clone, Default)]
 | 
			
		||||
struct KeyInfo {
 | 
			
		||||
    hosts: BTreeMap<String, HostInfo>,
 | 
			
		||||
    domain_to_name: BTreeMap<String, String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl KeyInfo {
 | 
			
		||||
    fn domain_to_name(&self, domain: &str) -> Option<&str> {
 | 
			
		||||
        self.domain_to_name.get(domain).map(|s| &**s)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn load() -> eyre::Result<Self> {
 | 
			
		||||
        let path = directories::ProjectDirs::from("", "Cyborus", "forgejo-cli")
 | 
			
		||||
            .ok_or_else(|| eyre!("Could not find data directory"))?
 | 
			
		||||
| 
						 | 
				
			
			@ -342,10 +333,13 @@ impl KeyInfo {
 | 
			
		|||
        } else {
 | 
			
		||||
            host_str.to_owned()
 | 
			
		||||
        };
 | 
			
		||||
        let name = self
 | 
			
		||||
            .domain_to_name(&domain)
 | 
			
		||||
            .ok_or_else(|| eyre!("unknown remote"))?;
 | 
			
		||||
 | 
			
		||||
        let (name, host) = self
 | 
			
		||||
            .hosts
 | 
			
		||||
            .get_key_value(&domain)
 | 
			
		||||
            .get_key_value(name)
 | 
			
		||||
            .ok_or_else(|| eyre!("not signed in to {domain}"))?;
 | 
			
		||||
        Ok((name, host, repo_from_url(&remote)?.into()))
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -381,9 +375,10 @@ fn repo_from_url(url: &Url) -> eyre::Result<&str> {
 | 
			
		|||
    Ok(repo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Default)]
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone)]
 | 
			
		||||
struct HostInfo {
 | 
			
		||||
    default: Option<String>,
 | 
			
		||||
    url: Url,
 | 
			
		||||
    users: BTreeMap<String, UserInfo>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -393,10 +388,7 @@ impl HostInfo {
 | 
			
		|||
            let (s, k) = self.users.first_key_value().unwrap();
 | 
			
		||||
            return Ok((s, k));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(default) = self
 | 
			
		||||
            .default
 | 
			
		||||
            .as_ref()
 | 
			
		||||
        {
 | 
			
		||||
        if let Some(default) = self.default.as_ref() {
 | 
			
		||||
            if let Some(default_info) = self.users.get(default) {
 | 
			
		||||
                return Ok((default, default_info));
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue