haven 0.1.2

Actix + React + Vite integration for server-rendered applications
Documentation
use std::fmt::{Display, Formatter};

use actix_web::HttpResponse;
use http::{HeaderMap, StatusCode};
use serde_json::json;
use thiserror::Error;

use crate::http_protocol::wants_page_protocol;

#[derive(Debug, Error)]
pub enum RendererError {
    #[error("runtime init failed: {0}")]
    RuntimeInit(String),
    #[error("render failed: {0}")]
    RenderFailed(String),
    #[error("render failed: {message}")]
    RenderFailedHtml { message: String, html: String },
    #[error("renderer worker channel closed")]
    ChannelClosed,
    #[error("serialization failed: {0}")]
    Serialization(String),
    #[error("javascript exception: {0}")]
    JavaScriptException(String),
    #[error("reload failed: {0}")]
    ReloadFailed(String),
    #[error("io failed: {0}")]
    Io(String),
    #[error("bundle build failed: {0}")]
    BundleBuildFailed(String),
    #[error("prerender failed: {0}")]
    PrerenderFailed(String),
}

impl RendererError {
    pub fn status_code(&self) -> StatusCode {
        StatusCode::INTERNAL_SERVER_ERROR
    }

    pub fn into_response_for_visit(self, is_page_protocol: bool) -> HttpResponse {
        if let Self::RenderFailedHtml { html, .. } = &self {
            if !is_page_protocol {
                return HttpResponse::build(
                    actix_web::http::StatusCode::from_u16(self.status_code().as_u16())
                        .unwrap_or(actix_web::http::StatusCode::INTERNAL_SERVER_ERROR),
                )
                .content_type("text/html; charset=utf-8")
                .body(html.clone());
            }
        }

        HttpResponse::build(
            actix_web::http::StatusCode::from_u16(self.status_code().as_u16())
                .unwrap_or(actix_web::http::StatusCode::INTERNAL_SERVER_ERROR),
        )
        .json(json!({
            "error": self.to_string(),
        }))
    }

    pub fn into_response_for_headers(self, headers: Option<&HeaderMap>) -> HttpResponse {
        self.into_response_for_visit(headers.is_some_and(wants_page_protocol))
    }
}

impl RendererError {
    pub fn into_actix_response_for_visit(self, is_page_protocol: bool) -> HttpResponse {
        self.into_response_for_visit(is_page_protocol)
    }

    pub fn into_actix_response_for_headers(self, headers: Option<&HeaderMap>) -> HttpResponse {
        self.into_response_for_headers(headers)
    }
}

impl From<std::io::Error> for RendererError {
    fn from(value: std::io::Error) -> Self {
        Self::Io(value.to_string())
    }
}

impl From<serde_json::Error> for RendererError {
    fn from(value: serde_json::Error) -> Self {
        Self::Serialization(value.to_string())
    }
}

impl Display for crate::renderer::RendererMode {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            crate::renderer::RendererMode::Development => f.write_str("development"),
            crate::renderer::RendererMode::Production => f.write_str("production"),
        }
    }
}