git-gemini-forge 0.6.6

A simple Gemini server that serves a read-only view of public repositories from a Git forge.
use super::converters::gitlab_project_id;
use crate::config::SecretHeader;
use crate::handlers::templates::PercentEncoded;
use crate::network::error;
use crate::network::net::Net;
use crate::network::responses::*;
use crate::network::{ForgeApi, ForgeApiKind};
use async_trait::async_trait;
use reqwest::header::HeaderName;
use url::Url;

/// Describes the GitLab API.
///
/// See https://docs.gitlab.com/ee/api/api_resources.html
#[derive(Clone, Debug)]
pub struct GitLabApi {
	_base_url: Url,
	_kind: ForgeApiKind,
	_private_token: Option<SecretHeader>,
}

impl GitLabApi {
	/// Constructs a reference to the API with the given forge URL and secret.
	pub fn from_url(forge_url: Url, private_token: &Option<SecretHeader>) -> Self {
		let base_url: Url = forge_url.join("/api/v4/").expect("Valid URL path");
		GitLabApi {
			_base_url: base_url,
			_kind: ForgeApiKind::GitLab,
			_private_token: private_token.clone(),
		}
	}
}

#[async_trait]
impl ForgeApi for GitLabApi {
	fn kind(&self) -> ForgeApiKind {
		self._kind
	}

	fn access_token_header_name(&self) -> HeaderName {
		HeaderName::from_static("Private-Token")
	}

	fn access_token_value(&self) -> Option<SecretHeader> {
		self._private_token.clone()
	}

	async fn check_access_token(&self, net: &Net) -> Result<(), error::Error> {
		match self.access_token_value() {
			None => Ok(()),
			Some(_) => super::get_authed_user(self, net).await.map(|_| ()),
		}
	}

	fn base_url(&self) -> &Url {
		&self._base_url
	}

	async fn get_branches(
		&self,
		net: &Net,
		username: &PercentEncoded,
		project_pathname: &PercentEncoded,
	) -> Result<Vec<Branch>, error::Error> {
		let project_id = gitlab_project_id(username, project_pathname);
		super::get_branches(self, net, &project_id).await
	}

	async fn get_contents(
		&self,
		net: &Net,
		username: &PercentEncoded,
		project_pathname: &PercentEncoded,
		branch_name: &PercentEncoded,
		item_path: &PercentEncoded,
	) -> Result<Contents<Item>, error::Error> {
		let project_id = gitlab_project_id(username, project_pathname);
		super::get_contents(self, net, &project_id, branch_name, item_path)
			.await
			.map(Into::into)
	}

	async fn get_file_tree(
		&self,
		net: &Net,
		username: &PercentEncoded,
		project_pathname: &PercentEncoded,
	) -> Result<Vec<Item>, error::Error> {
		let project_id = gitlab_project_id(username, project_pathname);
		super::get_file_tree(self, net, &project_id)
			.await
			.map(|tree| tree.iter().map(Into::into).collect())
	}

	async fn get_recent_repos(&self, net: &Net) -> Result<Vec<Repo>, error::Error> {
		super::get_recent_repos(self, net).await.map(|projects| {
			projects
				.iter()
				.filter(|p| p.default_branch.is_some()) // ignore empty projects
				.map(Into::into)
				.collect()
		})
	}

	async fn get_repo(
		&self,
		net: &Net,
		username: &PercentEncoded,
		project_pathname: &PercentEncoded,
	) -> Result<Repo, error::Error> {
		let project_id = gitlab_project_id(username, project_pathname);
		super::get_repo(self, net, &project_id)
			.await
			.map(Into::into)
	}

	async fn get_user_repos(
		&self,
		net: &Net,
		username: &PercentEncoded,
	) -> Result<Vec<Repo>, error::Error> {
		super::get_user_repos(self, net, username)
			.await
			.map(|projects| projects.iter().map(Into::into).collect())
	}

	async fn get_users(&self, net: &Net) -> Result<Vec<User>, error::Error> {
		super::get_users(self, net)
			.await
			.map(|users| users.iter().map(Into::into).collect())
	}

	async fn get_version(&self, net: &Net) -> Result<ServerVersion, error::Error> {
		super::get_version(self, net).await.map(Into::into)
	}
}