use std::cell::RefCell;
use std::rc::Rc;
pub type RenderFn = Rc<RefCell<dyn FnMut(&serde_json::Value) -> Result<String, RenderError>>>;
#[derive(Debug, thiserror::Error)]
pub enum RenderError {
#[error("render error: {0}")]
Render(String),
#[error("serialization error: {0}")]
Serialization(String),
#[error("{0}")]
Other(String),
}
impl From<serde_json::Error> for RenderError {
fn from(e: serde_json::Error) -> Self {
RenderError::Serialization(e.to_string())
}
}
pub fn from_fn<F>(f: F) -> RenderFn
where
F: FnMut(&serde_json::Value) -> Result<String, RenderError> + 'static,
{
Rc::new(RefCell::new(f))
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_from_fn_json() {
let render = from_fn(|data| {
serde_json::to_string_pretty(data)
.map_err(|e| RenderError::Serialization(e.to_string()))
});
let data = json!({"name": "test"});
let result = render.borrow_mut()(&data).unwrap();
assert!(result.contains("\"name\": \"test\""));
}
#[test]
fn test_from_fn_custom() {
let render = from_fn(|data| {
let name = data
.get("name")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
Ok(format!("Hello, {}!", name))
});
let data = json!({"name": "world"});
let result = render.borrow_mut()(&data).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn test_from_fn_mutable_state() {
let mut call_count = 0;
let render = from_fn(move |data| {
call_count += 1;
Ok(format!("Call {}: {}", call_count, data))
});
let data = json!({"key": "value"});
let result1 = render.borrow_mut()(&data).unwrap();
let result2 = render.borrow_mut()(&data).unwrap();
assert!(result1.contains("Call 1"));
assert!(result2.contains("Call 2"));
}
#[test]
fn test_render_error_from_serde() {
let err: RenderError = serde_json::from_str::<serde_json::Value>("invalid")
.unwrap_err()
.into();
assert!(matches!(err, RenderError::Serialization(_)));
}
}