// Back - An extremely simple git bug visualization system. Inspired by TVL's // panettone. // // Copyright (C) 2025 Benedikt Peetz // SPDX-License-Identifier: AGPL-3.0-or-later // // This file is part of Back. // // You should have received a copy of the License along with this program. // If not, see . use std::convert::Infallible; use bytes::Bytes; use git_bug::replica::entity::id::prefix::IdPrefix; use http::{Response, StatusCode, Version}; use http_body_util::{BodyExt, Full, combinators::BoxBody}; use vy::{IntoHtml, h1, p, pre}; use super::generate::templates::make_page; use crate::error; pub(super) fn html_response>(html_text: T) -> Response> { html_response_status(html_text, StatusCode::OK) } pub(super) fn html_response_status>( html_text: T, status: StatusCode, ) -> Response> { html_response_status_content_type(html_text, status, "text/html") } pub(super) fn redirect(target: &str) -> Response> { Response::builder() .status(StatusCode::MOVED_PERMANENTLY) .version(Version::HTTP_2) .header("Location", target) .body(full("")) .expect("This is hardcoded and will build") } pub(super) fn html_response_status_content_type>( html_text: T, status: StatusCode, content_type: &str, ) -> Response> { Response::builder() .status(status) .version(Version::HTTP_2) .header("Content-Type", format!("{content_type}; charset=utf-8")) .header("x-content-type-options", "nosniff") .header("x-frame-options", "SAMEORIGIN") .body(full(html_text)) .expect("This will always build") } pub(super) fn full>(chunk: T) -> BoxBody { Full::new(chunk.into()).boxed() } impl error::Error { #[allow(clippy::too_many_lines)] pub fn into_response(self) -> Response> { log::error!("{self}"); match self { error::Error::ConfigParse { .. } | error::Error::ProjectListRead { .. } | error::Error::ConfigRead { .. } | error::Error::RepoGetReferences(_) | error::Error::RepoIssueRead(_) | error::Error::RepoIdentityRead(_) | error::Error::RepoFind { .. } | error::Error::RepoRefsIter(_) | error::Error::RepoRefsPrefixed { .. } | error::Error::TcpBind { .. } | error::Error::RepoOpen { .. } | error::Error::TcpAccept { .. } => html_response_status( make_page( ( h1!("Internal server error"), pre!(format!("Error {}", self.to_string())), ), "Error page", Some("Error | Back"), ) .into_string(), StatusCode::INTERNAL_SERVER_ERROR, ), error::Error::NotGitBug { path } => html_response_status( make_page( ( h1!("Expectation Failed"), p!( "Repository '", path.display().to_string(), "' has no git bug data to show." ), ), "Error page", Some("Error | Back"), ) .into_string(), StatusCode::EXPECTATION_FAILED, ), error::Error::IssuesPrefixMissing { prefix, err } => html_response_status( make_page( ( h1!("Expectation Failed"), p!( "There is no issue associated with the prefix ", "'", prefix.to_string(), "': ", err.to_string() ), ), "Error page", Some("Error | Back"), ) .into_string(), StatusCode::EXPECTATION_FAILED, ), error::Error::IssuesPrefixParse { prefix, error } => html_response_status( make_page( ( h1!("Expectation Failed"), p!( "The prefix '", prefix, "' cannot be interperted as a prefix", ), p!(error.to_string()), p!( "The prefix is composed of ", IdPrefix::REQUIRED_LENGTH, " or more hex chars." ), ), "Error page", Some("Error | Back"), ) .into_string(), StatusCode::EXPECTATION_FAILED, ), error::Error::NotFound(path_buf) => html_response_status( make_page( ( h1!("Expectation Failed"), p!( "The path '", path_buf.display().to_string(), "' was unkonwn", ), ), "Error page", Some("Error | Back"), ) .into_string(), StatusCode::EXPECTATION_FAILED, ), error::Error::InvalidQuery { err, query } => html_response_status( make_page( ( h1!("Expectation Failed"), p!("The query '", query, "' was invalid",), p!(err.to_string()), ), "Error page", Some("Error | Back"), ) .into_string(), StatusCode::EXPECTATION_FAILED, ), } } }