use anyhow::Context;
use std::path::Path;
#[derive(Clone, Debug)]
pub struct IndexHtml {
pub(crate) head_before_title: String,
pub(crate) head_after_title: String,
pub(crate) title: String,
pub(crate) close_head: String,
pub(crate) post_main: String,
pub(crate) after_closing_body_tag: String,
}
impl IndexHtml {
pub fn new(contents: &str, root_id: &str) -> Result<IndexHtml, anyhow::Error> {
let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\""))
.with_context(|| format!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page."))?;
let post_main = post_main.split_once('>').with_context(|| {
format!("Failed to find closing > after id=\"{root_id}\" in index.html.")
})?;
let (pre_main, post_main) = (
pre_main.to_string() + &format!("id=\"{root_id}\"") + post_main.0 + ">",
post_main.1.to_string(),
);
let (head, close_head) = pre_main.split_once("</head>").with_context(|| {
format!("Failed to find closing </head> tag after id=\"{root_id}\" in index.html.")
})?;
let (head, close_head) = (head.to_string(), "</head>".to_string() + close_head);
let (post_main, after_closing_body_tag) =
post_main.split_once("</body>").with_context(|| {
format!("Failed to find closing </body> tag after id=\"{root_id}\" in index.html.")
})?;
let mut head_before_title = String::new();
let mut head_after_title = head;
let mut title = String::new();
if let Some((new_head_before_title, new_title)) = head_after_title.split_once("<title>") {
let (new_title, new_head_after_title) = new_title
.split_once("</title>")
.context("Failed to find closing </title> tag after <title> in index.html.")?;
title = format!("<title>{new_title}</title>");
head_before_title = new_head_before_title.to_string();
head_after_title = new_head_after_title.to_string();
}
Ok(IndexHtml {
head_before_title,
head_after_title,
title,
close_head,
post_main: post_main.to_string(),
after_closing_body_tag: "</body>".to_string() + after_closing_body_tag,
})
}
pub fn from_file(path: &Path, id: &str) -> Result<IndexHtml, anyhow::Error> {
let contents = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read index.html from {}", path.display()))?;
IndexHtml::new(&contents, id)
}
pub fn ssr_only() -> Self {
const DEFAULT: &str = r#"<!DOCTYPE html>
<html>
<head> </head>
<body>
<div id="main"></div>
</body>
</html>"#;
Self::new(DEFAULT, "main").expect("Failed to load default index.html")
}
}