-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add optional support for lax rendering and parsing #492
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,14 @@ use crate::model::{Object, ObjectView, Scalar, ScalarCow, Value, ValueCow, Value | |
use super::PartialStore; | ||
use super::Renderable; | ||
|
||
/// What mode to use when rendering. | ||
pub enum RenderingMode { | ||
/// Returns an error when a variable is not defined. | ||
Strict, | ||
/// Replaces missing variables with an empty string. | ||
Lax, | ||
} | ||
Comment on lines
+10
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to my comments about ParseMode, I feel like this might be too general |
||
|
||
/// State for rendering a template | ||
pub trait Runtime { | ||
/// Partial templates for inclusion. | ||
|
@@ -36,6 +44,9 @@ pub trait Runtime { | |
|
||
/// Unnamed state for plugins during rendering | ||
fn registers(&self) -> &Registers; | ||
|
||
/// Used to set the mode when rendering | ||
fn render_mode(&self) -> &RenderingMode; | ||
} | ||
|
||
impl<'r, R: Runtime + ?Sized> Runtime for &'r R { | ||
|
@@ -78,12 +89,17 @@ impl<'r, R: Runtime + ?Sized> Runtime for &'r R { | |
fn registers(&self) -> &super::Registers { | ||
<R as Runtime>::registers(self) | ||
} | ||
|
||
fn render_mode(&self) -> &RenderingMode { | ||
<R as Runtime>::render_mode(self) | ||
} | ||
} | ||
|
||
/// Create processing runtime for a template. | ||
pub struct RuntimeBuilder<'g, 'p> { | ||
globals: Option<&'g dyn ObjectView>, | ||
partials: Option<&'p dyn PartialStore>, | ||
render_mode: RenderingMode, | ||
} | ||
|
||
impl<'c, 'g: 'c, 'p: 'c> RuntimeBuilder<'g, 'p> { | ||
|
@@ -92,6 +108,7 @@ impl<'c, 'g: 'c, 'p: 'c> RuntimeBuilder<'g, 'p> { | |
Self { | ||
globals: None, | ||
partials: None, | ||
render_mode: RenderingMode::Strict, | ||
} | ||
} | ||
|
||
|
@@ -100,6 +117,7 @@ impl<'c, 'g: 'c, 'p: 'c> RuntimeBuilder<'g, 'p> { | |
RuntimeBuilder { | ||
globals: Some(values), | ||
partials: self.partials, | ||
render_mode: self.render_mode, | ||
} | ||
} | ||
|
||
|
@@ -108,6 +126,16 @@ impl<'c, 'g: 'c, 'p: 'c> RuntimeBuilder<'g, 'p> { | |
RuntimeBuilder { | ||
globals: self.globals, | ||
partials: Some(values), | ||
render_mode: self.render_mode, | ||
} | ||
} | ||
|
||
/// Initialize with the provided rendering mode. | ||
pub fn set_render_mode(self, mode: RenderingMode) -> RuntimeBuilder<'g, 'p> { | ||
RuntimeBuilder { | ||
globals: self.globals, | ||
partials: self.partials, | ||
render_mode: mode, | ||
} | ||
} | ||
|
||
|
@@ -116,6 +144,7 @@ impl<'c, 'g: 'c, 'p: 'c> RuntimeBuilder<'g, 'p> { | |
let partials = self.partials.unwrap_or(&NullPartials); | ||
let runtime = RuntimeCore { | ||
partials, | ||
render_mode: self.render_mode, | ||
..Default::default() | ||
}; | ||
let runtime = super::IndexFrame::new(runtime); | ||
|
@@ -208,6 +237,8 @@ pub struct RuntimeCore<'g> { | |
partials: &'g dyn PartialStore, | ||
|
||
registers: Registers, | ||
|
||
render_mode: RenderingMode, | ||
} | ||
|
||
impl<'g> RuntimeCore<'g> { | ||
|
@@ -268,13 +299,18 @@ impl<'g> Runtime for RuntimeCore<'g> { | |
fn registers(&self) -> &Registers { | ||
&self.registers | ||
} | ||
|
||
fn render_mode(&self) -> &RenderingMode { | ||
&self.render_mode | ||
} | ||
} | ||
|
||
impl<'g> Default for RuntimeCore<'g> { | ||
fn default() -> Self { | ||
Self { | ||
partials: &NullPartials, | ||
registers: Default::default(), | ||
render_mode: RenderingMode::Strict, | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ use liquid_core::error::Result; | |
use liquid_core::runtime; | ||
use liquid_core::runtime::PartialStore; | ||
use liquid_core::runtime::Renderable; | ||
use liquid_core::runtime::RenderingMode; | ||
|
||
pub struct Template { | ||
pub(crate) template: runtime::Template, | ||
|
@@ -14,16 +15,51 @@ pub struct Template { | |
impl Template { | ||
/// Renders an instance of the Template, using the given globals. | ||
pub fn render(&self, globals: &dyn crate::ObjectView) -> Result<String> { | ||
self.render_with_mode(globals, RenderingMode::Strict) | ||
} | ||
|
||
/// Renders an instance of the Template, using the given globals. | ||
pub fn render_to(&self, writer: &mut dyn Write, globals: &dyn crate::ObjectView) -> Result<()> { | ||
self.render_to_with_mode(writer, globals, RenderingMode::Strict) | ||
} | ||
|
||
/// Renders an instance of the Template, using the given globals in lax mode. | ||
pub fn render_lax(&self, globals: &dyn crate::ObjectView) -> Result<String> { | ||
self.render_with_mode(globals, RenderingMode::Lax) | ||
} | ||
|
||
Comment on lines
+22
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than having a bunch of different render funtions, should this be stored with the Parser and passed in when this is created? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually originally started that route but decided against it. When stored on the parser that means you're unable to determine which mode you'd want to use after the parser is created. Every template that's parsed would be stuck in that rendering mode. People would in theory, need to then create two separate parsers to work around that limitation. Admittedly, I'm not sure how frequent people would ever need to change the rendering mode of a parser. I don't personally have a use case to argue that. Another option is we could expose a function to set the rendering mode on the |
||
/// Renders an instance of the Template, using the given globals in lax mode. | ||
pub fn render_to_lax( | ||
&self, | ||
writer: &mut dyn Write, | ||
globals: &dyn crate::ObjectView, | ||
) -> Result<()> { | ||
self.render_to_with_mode(writer, globals, RenderingMode::Lax) | ||
} | ||
|
||
/// Renders an instance of the Template, using the given globals with the provided rendering mode. | ||
fn render_with_mode( | ||
&self, | ||
globals: &dyn crate::ObjectView, | ||
mode: RenderingMode, | ||
) -> Result<String> { | ||
const BEST_GUESS: usize = 10_000; | ||
let mut data = Vec::with_capacity(BEST_GUESS); | ||
self.render_to(&mut data, globals)?; | ||
self.render_to_with_mode(&mut data, globals, mode)?; | ||
|
||
Ok(convert_buffer(data)) | ||
} | ||
|
||
/// Renders an instance of the Template, using the given globals. | ||
pub fn render_to(&self, writer: &mut dyn Write, globals: &dyn crate::ObjectView) -> Result<()> { | ||
let runtime = runtime::RuntimeBuilder::new().set_globals(globals); | ||
/// Renders an instance of the Template, using the given globals with the provided rendering mode. | ||
fn render_to_with_mode( | ||
&self, | ||
writer: &mut dyn Write, | ||
globals: &dyn crate::ObjectView, | ||
mode: RenderingMode, | ||
) -> Result<()> { | ||
let runtime = runtime::RuntimeBuilder::new() | ||
.set_globals(globals) | ||
.set_render_mode(mode); | ||
let runtime = match self.partials { | ||
Some(ref partials) => runtime.set_partials(partials.as_ref()), | ||
None => runtime, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this should have
Copy
,Clone
,Debug
,PartialEq
,Eq
, andDefault