Handle variants of an enum
We have en enum that has 2 or more variants probably with differen attributes. We would like to be able to display the each variant in its own way.
Ideally we would probably be able to use a match inside the templates.
This is a solution that might be overengineered.
use liquid_core::{
Display_filter, Filter, FilterReflection, ParseFilter, Result, Runtime, Value, ValueView,
};
use serde::Serialize;
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(name = "type", description = "Type", parsed(TypeFilter))]
pub struct TypeStr;
#[derive(Debug, Default, Display_filter)]
#[name = "typestr"]
pub struct TypeFilter;
impl Filter for TypeFilter {
fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result<Value> {
let keys = ["Presentation", "Break"];
match input.as_object() {
Some(obj) => {
for key in keys {
if obj.contains_key(key) {
return Ok(Value::scalar(format!("{}", key)));
}
}
return Ok(Value::scalar("Unknown Item"));
}
None => Ok(Value::scalar("Not an object")),
}
}
}
#[derive(Serialize)]
struct Presentation {
title: String,
length: u32,
speaker: String,
}
#[derive(Serialize)]
struct Break {
length: u32,
}
#[derive(Serialize)]
enum Item {
Presentation(Presentation),
Break(Break),
}
fn main() {
let items = get_items();
let text = items_to_text(&items);
//println!("{}", text);
let expected_text = "\
Presentation: Introduction to Rust by Alice (30 mins)
Break: 15 mins
Presentation: Advanced Rust by Bob (45 mins)
";
assert_eq!(text, expected_text);
let html = render(&items);
let expected_html = "\
<ul>
<li>Introduction to Rust by Alice - (30 minutes)</li>
<li>Break (15 minutes)</li>
<li>Advanced Rust by Bob - (45 minutes)</li>
</ul>";
assert_eq!(html, expected_html);
//println!("{}", html);
}
fn get_items() -> Vec<Item> {
vec![
Item::Presentation(Presentation {
title: "Introduction to Rust".to_string(),
length: 30,
speaker: "Alice".to_string(),
}),
Item::Break(Break { length: 15 }),
Item::Presentation(Presentation {
title: "Advanced Rust".to_string(),
length: 45,
speaker: "Bob".to_string(),
}),
]
}
fn items_to_text(items: &Vec<Item>) -> String {
let mut result = String::new();
for item in items {
match item {
Item::Presentation(p) => {
result.push_str(&format!(
"Presentation: {} by {} ({} mins)\n",
p.title, p.speaker, p.length
));
}
Item::Break(b) => {
result.push_str(&format!("Break: {} mins\n", b.length));
}
}
}
result
}
fn render(items: &Vec<Item>) -> String {
let template = "\
<ul>
{%- for item in items -%}
{%- assign it = item | type -%}
{%- if it == \"Presentation\" %}
<li>{{item.Presentation.title}} by {{item.Presentation.speaker}} - ({{item.Presentation.length}} minutes)</li>
{%- elsif it == \"Break\" %}
<li>Break ({{item.Break.length}} minutes)</li>
{%- endif -%}
{% endfor %}
</ul>";
let template = liquid::ParserBuilder::with_stdlib()
.filter(TypeStr)
.build()
.unwrap()
.parse(template)
.unwrap();
let globals = liquid::object!({
"name": "Liquid",
"items": items,
});
let output = template.render(&globals).unwrap();
output
}