Skip to content

Commit

Permalink
feat(config): add SsrkitConfig for configurable cache sizes in island…
Browse files Browse the repository at this point in the history
… and template management

refactor(island): replace HashMap with LruCache for improved cache management in island rendering
refactor(template): replace HashMap with LruCache for improved cache management in template rendering
chore: remove unused tracing dependency from the project
  • Loading branch information
Jerome committed Jul 30, 2024
1 parent 2458a56 commit fcbd09b
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 39 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ regex = "1.10.5"
nanoid = "0.4.0"
indoc = "2.0.5"
lru = "0.12.3"
tracing = "0.1.40"
18 changes: 14 additions & 4 deletions src/island.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::config::SsrkitConfig;
use lru::LruCache;
use nanoid::nanoid;
use serde_json::Value;
use std::borrow::Cow;
Expand Down Expand Up @@ -62,7 +64,6 @@ impl IslandManifest {

pub struct ProcessContext {
pub path: String,
// 可以根据需要添加更多字段
}

pub trait IslandProcessor: Send + Sync {
Expand Down Expand Up @@ -183,19 +184,28 @@ impl Clone for IslandManager {
}
}

static ISLAND_CACHE: OnceLock<Mutex<HashMap<String, String>>> = OnceLock::new();
static ISLAND_CACHE: OnceLock<Mutex<LruCache<String, String>>> = OnceLock::new();
static CONFIG: OnceLock<SsrkitConfig> = OnceLock::new();

pub fn init_island_cache(config: &SsrkitConfig) {
let _ = CONFIG.set(config.clone());
}

pub fn get_or_render_island<F>(key: &str, render_fn: F) -> String
where
F: FnOnce() -> String,
{
let cache = ISLAND_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
let cache = ISLAND_CACHE.get_or_init(|| {
let binding = SsrkitConfig::default();
let config = CONFIG.get().unwrap_or(&binding);
Mutex::new(LruCache::new(config.island_cache_size))
});
let mut cache_guard = cache.lock().unwrap();
match cache_guard.get(key) {
Some(cached) => cached.clone(),
None => {
let rendered = render_fn();
cache_guard.insert(key.to_string(), rendered.clone());
cache_guard.put(key.to_string(), rendered.clone());
rendered
}
}
Expand Down
11 changes: 4 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ pub mod island;
pub mod params;
pub mod render;
pub mod template;
pub mod config;

// Re-export main types and traits
pub use island::{CombinedIslandProcessor, IslandManager, IslandProcessor, ProcessContext, get_or_render_island};
pub use params::{CombinedParamsProcessor, ParamsProcessor};
pub use render::{init_ssr, SsrRenderer, get_renderer};
pub use template::Template;
pub use config::SsrkitConfig;

// Re-export important types from serde_json that are commonly used
pub use serde_json::{Map, Value};
Expand All @@ -18,11 +20,6 @@ pub mod prelude {
pub use crate::params::{CombinedParamsProcessor, ParamsProcessor};
pub use crate::render::{init_ssr, SsrRenderer, get_renderer};
pub use crate::template::Template;
pub use crate::config::SsrkitConfig;
pub use serde_json::{Map, Value};
}

// If you have any error types, you might want to re-export them here
// pub use crate::error::SsrKitError;

// If you have any configuration structs, you might want to re-export them here
// pub use crate::config::SsrKitConfig;
}
30 changes: 12 additions & 18 deletions src/render.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::island::{IslandManager, IslandProcessor, ProcessContext};
use crate::island::{init_island_cache, IslandManager, IslandProcessor, ProcessContext};
use crate::params::ParamsProcessor;
use crate::template::Template;
use crate::template::{init_template_cache, Template};
use crate::SsrkitConfig;
use regex::Regex;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::OnceLock;
use tracing::{info, instrument};

// 全局靜態變量
static ISLAND_REGEX: OnceLock<Regex> = OnceLock::new();
Expand All @@ -21,7 +21,6 @@ pub struct SsrRenderer {
}

impl SsrRenderer {
#[instrument(skip(params_processor, island_manager, template))]
fn new(
params_processor: Box<dyn ParamsProcessor>,
island_manager: Arc<IslandManager>,
Expand All @@ -34,7 +33,6 @@ impl SsrRenderer {
}
}

#[instrument(skip(self, params, render_fn), fields(path = %path))]
pub fn render<F>(
&self,
path: &str,
Expand All @@ -45,31 +43,25 @@ impl SsrRenderer {
F: FnOnce(&str) -> Result<String, String>,
{
let processed_params = self.params_processor.process(path, &params);
info!("Params processed");

let props = serde_json::json!({
"url": path,
"params": processed_params,
});

let content = render_fn(&props.to_string())?;
info!("Render function called");

let mut rendered = serde_json::from_str::<Value>(&content)
.map_err(|e| format!("Failed to parse render result: {}", e))?;
info!("Render result parsed");

// 條件性島嶼處理
let mut used_islands = Vec::new();
if let Some(html) = rendered["html"].as_str() {
if html.contains("data-island") {
info!("Islands detected, starting replacement");
let (replaced_html, islands) = self.replace_island_placeholders(html)?;
rendered["html"] = Value::String(replaced_html);
used_islands = islands;
info!("Islands replaced");
} else {
info!("No islands detected, skipping replacement");
}
}

Expand All @@ -83,11 +75,9 @@ impl SsrRenderer {
};

let result = self.template.render(&rendered, &islands_json);
info!("Template rendered");
result
}

#[instrument(skip(self, html))]
fn replace_island_placeholders(&self, html: &str) -> Result<(String, Vec<String>), String> {
let re = ISLAND_REGEX.get().expect("Regex not initialized");
let mut result = html.to_string();
Expand All @@ -103,7 +93,6 @@ impl SsrRenderer {
let rendered_island = self.island_manager.render_island(island_id, &props)?;
result = result.replace(&cap[0], &rendered_island);
used_islands.push(island_id.to_string());
info!("Island rendered: {}", island_id);
}

Ok((result, used_islands))
Expand Down Expand Up @@ -138,22 +127,28 @@ impl SsrRenderer {
}
}

#[instrument(skip(params_processor_init, island_manager_init, template_init))]
pub fn init_ssr(
params_processor_init: impl FnOnce() -> Box<dyn ParamsProcessor>,
island_manager_init: impl FnOnce() -> IslandManager,
template_init: impl FnOnce() -> Template,
config: Option<&SsrkitConfig>,
) {
let config = config.cloned().unwrap_or_default();

// 初始化正則表達式
ISLAND_REGEX.get_or_init(|| {
Regex::new(r#"<div data-island="([^"]+)"(?: data-props='([^']*)')?></div>"#).unwrap()
});

// 初始化 IslandManager
ISLAND_MANAGER.get_or_init(|| Arc::new(island_manager_init()));
let island_manager = island_manager_init();
init_island_cache(&config);
ISLAND_MANAGER.get_or_init(|| Arc::new(island_manager));

// 初始化 Template
TEMPLATE.get_or_init(|| Arc::new(template_init()));
let template = template_init();
init_template_cache(&config);
TEMPLATE.get_or_init(|| Arc::new(template));

// 初始化 Renderer
RENDERER.get_or_init(|| {
Expand All @@ -164,7 +159,6 @@ pub fn init_ssr(
)
});

info!("SSR components initialized");
}

pub fn get_renderer() -> &'static SsrRenderer {
Expand Down
25 changes: 16 additions & 9 deletions src/template.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
use crate::config::SsrkitConfig;
use lru::LruCache;
use serde_json::Value;
use std::collections::HashSet;
use std::sync::OnceLock;
use std::sync::Mutex;
use lru::LruCache;
use std::num::NonZeroUsize;

static TEMPLATE_CACHE: OnceLock<Mutex<LruCache<String, String>>> = OnceLock::new();
static CONFIG: OnceLock<SsrkitConfig> = OnceLock::new();

pub fn init_template_cache(config: &SsrkitConfig) {
let _ = CONFIG.set(config.clone());
}

fn get_cache() -> &'static Mutex<LruCache<String, String>> {
TEMPLATE_CACHE.get_or_init(|| {
Mutex::new(LruCache::new(NonZeroUsize::new(100).unwrap()))
let binding = SsrkitConfig::default();
let config = CONFIG.get().unwrap_or(&binding);
Mutex::new(LruCache::new(config.template_cache_size))
})
}

Expand All @@ -23,7 +30,7 @@ impl Template {
pub fn render(&self, content: &Value, islands: &Value) -> Result<String, String> {
let cache_key = format!("{:?}:{:?}", content, islands);

// 嘗試從緩存中獲取
// Try to get from cache
if let Some(cached_html) = get_cache().lock().unwrap().get(&cache_key) {
return Ok(cached_html.clone());
}
Expand All @@ -36,22 +43,22 @@ impl Template {
let island_scripts = self.generate_island_scripts(islands);

let rendered_html = if html.trim().starts_with("<!DOCTYPE html>") || html.trim().starts_with("<html>") {
// 如果是完整的 HTML,我們只需要插入額外的內容
// If it's a complete HTML, we just need to insert extra content
let mut rendered_html = html.to_string();

// </head> 之前插入額外的頭部內容
// Insert extra head content before </head>
if let Some(head_end) = rendered_html.find("</head>") {
rendered_html.insert_str(head_end, &format!("{}<style>{}</style>{}", head_extra, css, island_scripts));
}

// <body> 之後插入額外的身體內容
// Insert extra body content after <body>
if let Some(body_start) = rendered_html.find("<body>") {
rendered_html.insert_str(body_start + 6, body_extra);
}

rendered_html
} else {
// 如果不是完整的 HTML,使用我們的模板
// If it's not a complete HTML, use our template
indoc::formatdoc! {r#"
<!DOCTYPE html>
<html>
Expand All @@ -71,7 +78,7 @@ impl Template {
let mut final_html = rendered_html;
self.replace_island_placeholders(&mut final_html, islands);

// 將結果存入緩存
// Store result in cache
get_cache().lock().unwrap().put(cache_key, final_html.clone());

Ok(final_html)
Expand Down

0 comments on commit fcbd09b

Please sign in to comment.