diff options
| author | Quin <trypsynth@gmail.com> | 2025-07-11 16:55:49 -0600 |
|---|---|---|
| committer | Quin <trypsynth@gmail.com> | 2025-07-11 16:55:49 -0600 |
| commit | 38be7b73656d70e21118e1a8b7b6b21c8c20a849 (patch) | |
| tree | 644b2f3c2fb8f09d499b7d3f3a0c55eb5e9541f2 /htmlua-parser | |
| parent | c8ffbb2af0eae39b897ff3b1b6c8263181fdd8d4 (diff) | |
Basic toml config support.
Diffstat (limited to 'htmlua-parser')
| -rw-r--r-- | htmlua-parser/Cargo.toml | 4 | ||||
| -rw-r--r-- | htmlua-parser/src/config.rs | 98 | ||||
| -rw-r--r-- | htmlua-parser/src/helpers.rs | 10 | ||||
| -rw-r--r-- | htmlua-parser/src/lib.rs | 3 | ||||
| -rw-r--r-- | htmlua-parser/src/render.rs | 28 | ||||
| -rw-r--r-- | htmlua-parser/src/serve.rs | 33 |
6 files changed, 147 insertions, 29 deletions
diff --git a/htmlua-parser/Cargo.toml b/htmlua-parser/Cargo.toml index 28416b7..9e5018e 100644 --- a/htmlua-parser/Cargo.toml +++ b/htmlua-parser/Cargo.toml @@ -5,14 +5,16 @@ edition = "2024" [dependencies] anyhow = "1.0.98" +dirs = "6.0.0" html5ever = "0.35.0" kuchikiki = "0.8.2" markup5ever = "0.11.0" mlua = { version = "0.10.5", features = ["lua54", "vendored", "serialize"] } pulldown-cmark = "0.13.0" +serde = { version = "1.0.219", features = ["derive"] } syntect = "5.2.0" tendril = "0.4.3" - +toml = "0.9.2" [lints.clippy] pedantic = { level = "warn", priority = -1 } diff --git a/htmlua-parser/src/config.rs b/htmlua-parser/src/config.rs new file mode 100644 index 0000000..70ea7a8 --- /dev/null +++ b/htmlua-parser/src/config.rs @@ -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() } +} diff --git a/htmlua-parser/src/helpers.rs b/htmlua-parser/src/helpers.rs index 90439f7..bd99f81 100644 --- a/htmlua-parser/src/helpers.rs +++ b/htmlua-parser/src/helpers.rs @@ -1,15 +1,15 @@ -use std::{fs::File, io::{BufRead, BufReader, Read, Seek, SeekFrom}, path::PathBuf}; +use std::{ + fs::File, + io::{BufRead, BufReader, Read, Seek, SeekFrom}, + path::PathBuf, +}; use anyhow::Result; 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); diff --git a/htmlua-parser/src/lib.rs b/htmlua-parser/src/lib.rs index cd87be2..525486e 100644 --- a/htmlua-parser/src/lib.rs +++ b/htmlua-parser/src/lib.rs @@ -1,3 +1,4 @@ +pub mod config; +pub mod helpers; pub mod render; pub mod serve; -pub mod helpers; diff --git a/htmlua-parser/src/render.rs b/htmlua-parser/src/render.rs index 6444d60..9360f16 100644 --- a/htmlua-parser/src/render.rs +++ b/htmlua-parser/src/render.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, fmt::Write, fs::read_to_string, path::PathBuf, rc::Rc}; +use std::{cell::RefCell, fmt::Write, path::PathBuf, rc::Rc}; use anyhow::{Result, anyhow}; use kuchikiki::{NodeRef, traits::TendrilSink}; @@ -12,7 +12,7 @@ use syntect::{ util::LinesWithEndings, }; -use crate::helpers::read_doc_from_file; +use crate::{helpers::read_doc_from_file, serve::get_config}; fn create_htmlua_stdlib(l: &Lua, stdout: &Rc<RefCell<String>>) -> mlua::Result<Table> { let t = l.create_table()?; @@ -108,7 +108,11 @@ pub fn expand_template(document: NodeRef, component_path: &PathBuf, include_from let exported = from_node .select_first(format!("exportelement.{name}").as_str()) .map_err(|()| anyhow!("Error finding exportelement"))?; - exported.as_node().children().rev().for_each(|c| i.as_node().insert_after(c)); + exported + .as_node() + .children() + .rev() + .for_each(|c| i.as_node().insert_after(c)); } } while let Ok(i) = document.select_first("includeelement") { @@ -130,8 +134,13 @@ pub fn expand_template(document: NodeRef, component_path: &PathBuf, include_from item_path.push(include_path); let new_node = read_doc_from_file(item_path)?; let replaced_node = expand_template(new_node, component_path, Some(i.as_node()))?; - replaced_node.select_first("html").map_err(|()| anyhow!("Error finding html"))?.as_node().children().rev().for_each(|c| i.as_node().insert_after(c)); - + replaced_node + .select_first("html") + .map_err(|()| anyhow!("Error finding html"))? + .as_node() + .children() + .rev() + .for_each(|c| i.as_node().insert_after(c)); } } while let Ok(i) = document.select_first("include") { @@ -141,16 +150,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() { @@ -158,7 +166,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 diff --git a/htmlua-parser/src/serve.rs b/htmlua-parser/src/serve.rs index 06de0ea..9821210 100644 --- a/htmlua-parser/src/serve.rs +++ b/htmlua-parser/src/serve.rs @@ -1,20 +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)?; |
