🏡 index : ~doyle/rgit.git

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

use askama::Template;
use axum::{
    extract::Query,
    response::{IntoResponse, 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(default)]
    raw: bool,
    #[serde(rename = "h")]
    branch: Option<Arc<str>>,
}

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,
    pub branch: Option<Arc<str>>,
}

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

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, query.branch.clone()).await?;

    Ok(
        match open_repo
            .path(child_path, query.id.as_deref(), !query.raw)
            .await?
        {
            PathDestination::Tree(items) => into_response(&TreeView {
                repo,
                items,
                branch: query.branch.clone(),
                query,
            }),
            PathDestination::File(file) if query.raw => file.content.into_response(),
            PathDestination::File(file) => into_response(&FileView {
                repo,
                file,
                branch: query.branch,
            }),
        },
    )
}