diff --git a/examples/techdemo/src/main.rs b/examples/techdemo/src/main.rs index 59fadc8..2a03197 100644 --- a/examples/techdemo/src/main.rs +++ b/examples/techdemo/src/main.rs @@ -502,9 +502,8 @@ fn board_view(demo: &DemoState) -> BoardLanes { } fn render_board(demo: &DemoState) -> SafeHtml { - // req: html_safety/002 req: view/001 - let html = render_template(&board_view(demo)); - SafeHtml::trusted(ui::issue_lane::lower_html(ui::issue_card::lower_html(html))) + // req: html_safety/002 req: view/001 req: component/003 + ui::render_html(&board_view(demo)) } fn issue_card(item: &WorkItem, selected: bool) -> IssueCard { @@ -584,8 +583,8 @@ fn architecture_activity() -> SafeHtml { } fn render_control_center(page: ControlCenter) -> String { - // req: html_safety/002 req: view/001 - ui::control_center::lower_html(render_template(&page)) + // req: html_safety/002 req: view/001 req: component/003 + ui::control_center::render_html(&page).into_string() } fn render_template(template: &impl Hemplate) -> String { diff --git a/slhx-build/src/lib.rs b/slhx-build/src/lib.rs index 3c27714..6795469 100644 --- a/slhx-build/src/lib.rs +++ b/slhx-build/src/lib.rs @@ -351,8 +351,8 @@ impl Resources { if global_exports { self.push_component_refs(&mut out, 0); self.push_resource_modules(&mut out, None, 0); - self.push_lowering_api(&mut out, None, 0); } + self.push_lowering_api(&mut out, None, 0); for component in self.component_names() { out.push_str("\n#[allow(non_upper_case_globals)]\n"); @@ -524,6 +524,11 @@ impl Resources { } out.push_str(&format!("__slhx_lower_html(html.as_ref(), &{table_name})\n")); out.push_str(&format!("{pad}}}\n\n")); + out.push_str(&format!("{pad}pub fn render_html(view: &impl ::hemplate::Hemplate) -> ::slhx::SafeHtml {{\n")); + out.push_str(&format!("{pad} let mut html = ::std::string::String::new();\n")); + out.push_str(&format!("{pad} ::hemplate::Hemplate::render_into(view, &mut html).expect(\"slhx hemplate view renders\");\n")); + out.push_str(&format!("{pad} ::slhx::SafeHtml::trusted(lower_html(html))\n")); + out.push_str(&format!("{pad}}}\n\n")); self.push_lowering_table(out, component, indent, &table_name); } @@ -1293,6 +1298,7 @@ mod tests { assert!(generated.contains("pub mod todo")); assert!(generated.contains("pub const ALL_IDS")); assert!(generated.contains("pub fn lower_html")); + assert!(generated.contains("pub fn render_html(view: &impl ::hemplate::Hemplate) -> ::slhx::SafeHtml")); assert!(generated.contains("data-slhx-slot")); assert!(generated.contains("data-slhx-form")); assert!(generated.contains("data-sid")); @@ -1357,7 +1363,8 @@ mod tests { let generated = std::fs::read_to_string(out.join("slhx.generated.rs")).unwrap(); assert!(!generated.contains("\n#[allow(non_upper_case_globals)]\npub mod components")); assert!(!generated.contains("\n#[allow(non_upper_case_globals)]\npub mod slots")); - assert!(!generated.contains("\npub fn lower_html")); + assert!(generated.contains("\npub fn lower_html")); + assert!(generated.contains("\npub fn render_html(view: &impl ::hemplate::Hemplate) -> ::slhx::SafeHtml")); assert!(generated.contains("pub mod todo")); assert!(generated.contains(" pub const COMPONENT: ::slhx::ComponentRef = ::slhx::ComponentRef::new(\"todo\")")); assert!(generated.contains(" pub mod slots")); @@ -1476,10 +1483,15 @@ mod slhx {{ impl CssClass {{ pub const fn new(name: &'static str) -> Self {{ Self(name) }} pub const fn as_str(self) -> &'static str {{ self.0 }} }} #[derive(Clone, Copy)] pub struct ComponentRef(&'static str); impl ComponentRef {{ pub const fn new(name: &'static str) -> Self {{ Self(name) }} pub const fn as_str(self) -> &'static str {{ self.0 }} }} + pub struct SafeHtml(String); + impl SafeHtml {{ pub fn trusted(html: impl Into) -> Self {{ Self(html.into()) }} }} pub struct FormContract {{ pub fields: &'static [FormField] }} pub struct FormField {{ pub name: &'static str, pub kind: FormControlKind, pub required: bool }} pub enum FormControlKind {{ Text, Number {{ min: Option<&'static str>, max: Option<&'static str>, step: Option<&'static str> }}, Checkbox, Radio, Select {{ multiple: bool }}, TextArea, File, Hidden, Submit, Other {{ tag: &'static str, input_type: Option<&'static str> }} }} }} +mod hemplate {{ + pub trait Hemplate {{ fn render_into(&self, out: &mut String) -> Result<(), ()>; }} +}} {generated} fn main() {{ assert_eq!(classes::drag_handle.as_str(), "drag-handle"); @@ -1743,10 +1755,15 @@ mod slhx {{ impl Form {{ pub const fn new(_: u32) -> Self {{ Self(::std::marker::PhantomData) }} }} #[derive(Clone, Copy)] pub struct ComponentRef(&'static str); impl ComponentRef {{ pub const fn new(name: &'static str) -> Self {{ Self(name) }} pub const fn as_str(self) -> &'static str {{ self.0 }} }} + pub struct SafeHtml(String); + impl SafeHtml {{ pub fn trusted(html: impl Into) -> Self {{ Self(html.into()) }} }} pub struct FormContract {{ pub fields: &'static [FormField] }} pub struct FormField {{ pub name: &'static str, pub kind: FormControlKind, pub required: bool }} pub enum FormControlKind {{ Text, Number {{ min: Option<&'static str>, max: Option<&'static str>, step: Option<&'static str> }}, Checkbox, Radio, Select {{ multiple: bool }}, TextArea, File, Hidden, Submit, Other {{ tag: &'static str, input_type: Option<&'static str> }} }} }} +mod hemplate {{ + pub trait Hemplate {{ fn render_into(&self, out: &mut String) -> Result<(), ()>; }} +}} {generated} fn main() {{ let html = todo::lower_html(r#"
  • "#);