Rust Handlebars 动态选择子模板

问题描述 投票:0回答:1

想象一下,通过

Document
 的特征实现,动态包含 
Header
Paragraph
 等对象的 
Element

在执行时,我希望把手能够渲染文档及其子文档。因此,应该根据模板的类型来选择模板

Element

use std::{cell::RefCell, error::Error, rc::Rc};

use handlebars::Handlebars;
use serde::Serialize;

pub trait Element: std::fmt::Debug + erased_serde::Serialize {
    
}
erased_serde::serialize_trait_object!(Element);

#[derive(Debug, Serialize)]
pub struct Document {
    pub content: Vec<Rc<RefCell<dyn Element>>>
}

impl Element for Document { }

#[derive(Debug, Serialize)]
pub struct Header {
    content: String
}

impl Element for Header { }

#[derive(Debug, Serialize)]
pub struct Paragraph {
    content: String
}

impl Element for Paragraph { }

fn main()  -> Result<(), Box<dyn Error>> {
    let mut handlebars = Handlebars::new();

    handlebars.register_template_string("Header", "<h1>{{content}}</h1>".to_string())?;
    handlebars.register_template_string("Paragraph", "<p>{{content}}</p>".to_string())?;

    handlebars.register_template_string("Document", "{{#each content}}{{this}}\n{{/each}}".to_string())?;

    let document = Document {
        content: vec![
            Rc::new(RefCell::new(Header { content: "Title".to_string()})),
            Rc::new(RefCell::new(Paragraph { content: "Text".to_string()})),
        ]
    };
    
    println!("{:?}", document);
    println!("{}", handlebars.render("Document", &document)?);

    Ok(())
  
}

调试按预期工作

Document { content: [RefCell { value: Header { content: "Title" } }, RefCell { value: Paragraph { content: "Text" } }] }

但是车把只是渲染

[object]
,而不是在该对象上使用模板

[object]
[object]

我想要实现的输出:

<h1>Title</h1>
<p>Text<p>

如何实现这一目标?如何告诉车把根据对象类型/名称自动选择相应的模板?

rust handlebars.js
1个回答
0
投票

我自己目前想到的唯一解决方案是在特质

render
级别引入一个
Element
函数,最初在文档上调用它并递归地向下渲染它。

不幸的是,这需要我创建另一个数据结构,例如 json 或 btree 映射,这对我来说似乎是一个很大的开销。

use handlebars::{Handlebars, RenderError};
use serde::Serialize;
use serde_json::json;
use std::{cell::RefCell, error::Error, rc::Rc};

pub trait Element: std::fmt::Debug + erased_serde::Serialize {
    fn render(&self, handlebars: &Handlebars) -> Result<String, RenderError>;
}
erased_serde::serialize_trait_object!(Element);

#[derive(Debug, Serialize)]
pub struct Div {
    pub content: Vec<Rc<RefCell<dyn Element>>>,
}

impl Element for Div {
    fn render(&self, handlebars: &Handlebars) -> Result<String, RenderError> {
        let mut children = Vec::new();
        for child in self.content.iter() {
            children.push(child.as_ref().borrow().render(&handlebars)?);
        }
        handlebars.render("Div", &json!({"content": children}))
    }
}

#[derive(Debug, Serialize)]
pub struct Header {
    content: String,
}

impl Element for Header {
    fn render(&self, handlebars: &Handlebars) -> Result<String, RenderError> {
        handlebars.render("Header", &json!({"content": &self.content}))
    }
}

#[derive(Debug, Serialize)]
pub struct Paragraph {
    content: String,
}

impl Element for Paragraph {
    fn render(&self, handlebars: &Handlebars) -> Result<String, RenderError> {
        handlebars.render("Paragraph", &json!({"content": &self.content}))
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let mut handlebars = Handlebars::new();
    handlebars.register_template_string("Header", "<h1>{{{content}}}</h1>".to_string())?;
    handlebars.register_template_string("Paragraph", "<p>{{{content}}}</p>".to_string())?;
    handlebars.register_template_string(
        "Div",
        "<div>{{#each content}}{{{this}}}{{/each}}</div>".to_string(),
    )?;

    let div = Div {
        content: vec![
            Rc::new(RefCell::new(Header {
                content: "Title".to_string(),
            })),
            Rc::new(RefCell::new(Paragraph {
                content: "Paragraph One".to_string(),
            })),
            Rc::new(RefCell::new(Div {
                content: vec![Rc::new(RefCell::new(Paragraph {
                    content: "Nested Paragraph".to_string(),
                }))],
            })),
            Rc::new(RefCell::new(Paragraph {
                content: "Paragraph Two".to_string(),
            })),
        ],
    };

    println!("{}", div.render(&handlebars).unwrap());

    Ok(())
}

© www.soinside.com 2019 - 2024. All rights reserved.