git-gemini-forge 0.6.1

A simple Gemini server that serves a read-only view of public repositories from a Git forge.
pub mod net;
pub mod robotstxt;
pub mod user_agent;

pub mod error;
pub mod responses;

// Forge APIs:
pub mod forgejo;
pub mod gitlab;

use crate::handlers::templates::PercentEncoded;
use isahc::http::{HeaderName, header::AUTHORIZATION};
use net::Net;
use secrecy::SecretString;
use std::fmt::Debug;
use strum_macros::{Display, EnumIter, EnumString};
use url::Url;
pub use user_agent::user_agent;

// Describe the interfaces we expect:
#[derive(Clone, Copy, Debug, Display, EnumIter, EnumString, PartialEq)]
#[strum(ascii_case_insensitive)] // See https://docs.rs/strum/latest/strum/additional_attributes/index.html
pub enum ForgeApiKind {
	Forgejo,
	Gitea,
	GitLab,
}

/// Describes an interface by which we expect to get certain details
/// from a git forge. Implementations usually make REST calls via HTTP.
pub trait ForgeApi
where
	Self: Sync + Send,
{
	/// The kind of server software that serves this API.
	fn kind(&self) -> ForgeApiKind;

	/// The URL at which to communicate with the API. (e.g. "https://git.average.name/api/v1/")
	fn base_url(&self) -> &Url;

	/// Retrieves branches for the given repository.
	fn get_branches(
		&self,
		net: &Net,
		username: &PercentEncoded,
		repo_name: &PercentEncoded,
	) -> Result<Vec<responses::Branch>, error::Error>;

	/// Retrieves the file or list of files at the given path.
	fn get_contents(
		&self,
		net: &Net,
		username: &PercentEncoded,
		repo_name: &PercentEncoded,
		branch_name: &PercentEncoded,
		item_path: &PercentEncoded,
	) -> Result<responses::Contents<responses::Item>, error::Error>;

	/// Retrieves a list of files at the top-level of the given repository from the given API.
	fn get_file_tree(
		&self,
		net: &Net,
		username: &PercentEncoded,
		repo_name: &PercentEncoded,
	) -> Result<Vec<responses::Item>, error::Error>;

	/// Retrieves the 12 most recent repositories.
	fn get_recent_repos(&self, net: &Net) -> Result<Vec<responses::Repo>, error::Error>;

	/// Retrieves repository metadata.
	fn get_repo(
		&self,
		net: &Net,
		username: &PercentEncoded,
		repo_name: &PercentEncoded,
	) -> Result<responses::Repo, error::Error>;

	/// Retrieves the user's repositories.
	fn get_user_repos(
		&self,
		net: &Net,
		username: &PercentEncoded,
	) -> Result<Vec<responses::Repo>, error::Error>;

	/// Retrieves the users.
	fn get_users(&self, net: &Net) -> Result<Vec<responses::User>, error::Error>;

	/// Retrieves the forge version.
	fn get_version(&self, net: &Net) -> Result<responses::ServerVersion, error::Error>;

	/// Makes an authenticated API request to determine whether our access token is valid.
	/// Returns [`Ok`] if the token is valid or was not provided (we assume it isn't needed),
	/// and [`Err`] with [`error::Error::Unauthorized`] if not. Other relevant errors may be
	/// returned as well, such as [`error::Error::NetworkFailure`] or [`error::Error::Restricted`].
	fn check_access_token(&self, _net: &Net) -> Result<(), error::Error>;

	/// The header name to use for authenticating requests.
	fn access_token_header_name(&self) -> HeaderName {
		AUTHORIZATION
	}

	/// The access token with which to authenticate requests. If [`None`]
	/// is returned, then no additional auth header will be sent.
	fn access_token_value(&self) -> Option<SecretString> {
		None
	}
}