🏡 index : ~doyle/rgit.git

use std::{
    fmt::{Display, Formatter},
    sync::Arc,
};

use askama::Template;
use axum::{extract::Query, http, response::IntoResponse, response::Response, Extension};
use serde::Deserialize;

use crate::{
    git::{FileWithContent, PathDestination, TreeItem},
    into_response,
    methods::{
        filters,
        repo::{ChildPath, Repository, RepositoryPath, Result},
    },
    Git,
};

#[derive(Deserialize)]
pub struct UriQuery {
    id: Option<String>,
    #[serde(rename = "h")]
    branch: Option<String>,
    #[serde(default)]
    raw: bool,
}

impl Display for UriQuery {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let mut prefix = "?";

        if let Some(id) = self.id.as_deref() {
            write!(f, "{prefix}id={id}")?;
            prefix = "&";
        }

        if let Some(branch) = self.branch.as_deref() {
            write!(f, "{prefix}h={branch}")?;
        }

        Ok(())
    }
}

#[derive(Template)]
#[template(path = "repo/tree.html")]
#[allow(clippy::module_name_repetitions)]
pub struct TreeView {
    pub repo: Repository,
    pub items: Vec<TreeItem>,
    pub query: UriQuery,
}

#[derive(Template)]
#[template(path = "repo/file.html")]
pub struct FileView {
    pub repo: Repository,
    pub file: FileWithContent,
}

pub async fn handle(
    Extension(repo): Extension<Repository>,
    Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
    Extension(ChildPath(child_path)): Extension<ChildPath>,
    Extension(git): Extension<Arc<Git>>,
    Query(query): Query<UriQuery>,
) -> Result<Response> {
    let open_repo = git.repo(repository_path).await?;

    Ok(
        match open_repo
            .path(
                child_path,
                query.id.as_deref(),
                query.branch.clone(),
                !query.raw,
            )
            .await?
        {
            PathDestination::Tree(items) => into_response(&TreeView { repo, items, query }),
            PathDestination::File(file) if query.raw => {
                let headers = [(
                    http::header::CONTENT_TYPE,
                    http::HeaderValue::from_static("text/plain"),
                )];

                (headers, file.content).into_response()
            }
            PathDestination::File(file) => into_response(&FileView { repo, file }),
        },
    )
}