Merge branch 'main' into httpstdlib

This commit is contained in:
gyoder 2025-07-12 22:02:53 -06:00
commit 5ed6fdebb4
7 changed files with 257 additions and 27 deletions

View file

@ -0,0 +1,98 @@
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::{fs, path::PathBuf};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Config {
pub paths: PathConfig,
pub server: ServerConfig,
pub syntax_highlighting: SyntaxConfig,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct PathConfig {
pub pages: PathBuf,
pub components: PathBuf,
pub themes: PathBuf,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct SyntaxConfig {
pub default_theme: String,
pub load_custom_themes: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
paths: PathConfig {
pages: PathBuf::from("/var/www/htmlua/pages"),
components: PathBuf::from("/var/www/htmlua/components"),
themes: PathBuf::from("/var/www/htmlua/themes"),
},
server: ServerConfig {
host: "127.0.0.1".to_string(),
port: 8080,
},
syntax_highlighting: SyntaxConfig {
default_theme: "base16-ocean.dark".to_string(),
load_custom_themes: true,
},
}
}
}
impl Config {
pub fn load() -> Result<Self> {
let config_path = Self::get_config_path();
if config_path.exists() {
let config_content = fs::read_to_string(&config_path)
.with_context(|| format!("Failed to read config file: {}", config_path.display()))?;
let config: Config = toml::from_str(&config_content)
.with_context(|| format!("Failed to parse config file: {}", config_path.display()))?;
Ok(config)
} else {
let default_config = Config::default();
if let Some(parent) = config_path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Failed to create config directory: {}", parent.display()))?;
}
let config_content =
toml::to_string_pretty(&default_config).context("Failed to serialize default config")?;
fs::write(&config_path, config_content)
.with_context(|| format!("Failed to write default config to: {}", config_path.display()))?;
Ok(default_config)
}
}
fn get_config_path() -> PathBuf {
if cfg!(windows) {
dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("C:\\ProgramData"))
.join("htmlua")
.join("config.toml")
} else {
PathBuf::from("/etc/htmlua.toml")
}
}
pub fn save(&self) -> Result<()> {
let config_path = Self::get_config_path();
if let Some(parent) = config_path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Failed to create config directory: {}", parent.display()))?;
}
let config_content = toml::to_string_pretty(self).context("Failed to serialize config")?;
fs::write(&config_path, config_content)
.with_context(|| format!("Failed to write config to: {}", config_path.display()))?;
Ok(())
}
pub fn config_file_path() -> PathBuf { Self::get_config_path() }
}

View file

@ -9,9 +9,6 @@ use kuchikiki::NodeRef;
use markup5ever::{LocalName, Namespace, QualName};
use tendril::TendrilSink;
pub fn read_doc_from_file(path: PathBuf) -> Result<NodeRef> {
let mut file = File::open(path)?;
let mut reader = BufReader::new(&mut file);

View file

@ -1,4 +1,5 @@
pub mod helpers;
pub mod htmlua_stdlib;
pub mod config;
pub mod render;
pub mod serve;

View file

@ -17,7 +17,7 @@ use syntect::{
util::LinesWithEndings,
};
use crate::{helpers::read_doc_from_file, htmlua_stdlib::create_htmlua_stdlib};
use crate::{helpers::read_doc_from_file, htmlua_stdlib::create_htmlua_stdlib, serve::get_config};
fn build_lua_with_stdout(stdout: &Rc<RefCell<String>>) -> Result<Lua> {
@ -134,16 +134,15 @@ pub fn expand_template(document: NodeRef, component_path: &PathBuf, include_from
}
pub fn process_syntax_highlighting(document: NodeRef) -> Result<NodeRef> {
let config = get_config();
let ps = SyntaxSet::load_defaults_newlines();
let mut ts = ThemeSet::load_defaults();
let syntax_elements: Vec<_> = match document.select("syntaxhighlight") {
Ok(e) => e.collect(),
Err(()) => return Err(anyhow!("Unable to find syntaxhighlight elements")),
};
if !syntax_elements.is_empty() {
// TODO: read from config
let themes = PathBuf::from("/var/www/htmlua/themes");
let _ = ts.add_from_folder(themes);
if !syntax_elements.is_empty() && config.syntax_highlighting.load_custom_themes {
let _ = ts.add_from_folder(&config.paths.themes);
}
for node in syntax_elements {
let attrs = match node.as_node().as_element() {
@ -151,7 +150,7 @@ pub fn process_syntax_highlighting(document: NodeRef) -> Result<NodeRef> {
None => continue,
};
let language = attrs.get("lang").unwrap_or("text");
let theme_name = attrs.get("theme").unwrap_or("base16-ocean.dark");
let theme_name = attrs.get("theme").unwrap_or(&config.syntax_highlighting.default_theme);
if let Some(text_node) = node.as_node().first_child() {
if let Some(code_text) = text_node.as_text() {
let syntax = ps

View file

@ -1,19 +1,29 @@
use crate::helpers::read_doc_from_file;
use super::render::{execute_lua, expand_template, process_markdown, process_syntax_highlighting};
use crate::{
config::Config,
helpers::read_doc_from_file,
render::{execute_lua, expand_template, process_markdown, process_syntax_highlighting},
};
use anyhow::Result;
use std::path::{Path, PathBuf};
use std::{path::Path, sync::OnceLock};
static CONFIG: OnceLock<Config> = OnceLock::new();
pub fn get_config() -> &'static Config {
CONFIG.get_or_init(|| {
Config::load().unwrap_or_else(|e| {
eprintln!("Warning: Failed to load config: {}", e);
eprintln!("Using default configuration");
Config::default()
})
})
}
pub fn serve_content(request_uri: &str) -> Result<String> {
// TODO: read from config
let pages = PathBuf::from("/var/www/htmlua/pages");
let components = PathBuf::from("/var/www/htmlua/components");
let config = get_config();
let safe_path = Path::new(request_uri).strip_prefix("/")?;
let page_path = pages.join(safe_path);
let page_path = config.paths.pages.join(safe_path);
let doc = read_doc_from_file(page_path)?;
let full_doc = expand_template(doc, &components, None)?;
let full_doc = expand_template(doc, &config.paths.components, None)?;
let markdown_doc = process_markdown(full_doc)?;
let highlighted_doc = process_syntax_highlighting(markdown_doc)?;
let executed_doc = execute_lua(highlighted_doc)?;