///|
/// An abstraction over output targets with support for capturing.
///
/// Captures nest: `begin_capture` starts a new capture (or a discarding
/// capture) and `end_capture` returns what was captured as a value.
pub struct Output {
priv target : StringBuilder
/// An optional sink that receives the (non captured) output instead of
/// `target` when rendering in streaming mode.
priv sink : ((StringView) -> Unit raise)?
priv capture_stack : Array[StringBuilder?]
}
///|
fn Output::new(target : StringBuilder) -> Output {
{ target, sink: None, capture_stack: [], }
}
///|
fn Output::with_sink(sink : (StringView) -> Unit raise) -> Output {
{ target: StringBuilder(), sink: Some(sink), capture_stack: [], }
}
///|
fn Output::write_to_root(
self : Output,
s : StringView,
) -> Unit raise TemplateError {
match self.sink {
None => self.target.write_view(s)
Some(sink) =>
sink(s) catch {
err =>
raise TemplateError::new(
WriteFailure,
"error during rendering: \{err}",
)
}
}
}
///|
/// An output that discards everything.
fn Output::null() -> Output {
{ target: StringBuilder(), sink: None, capture_stack: [None], }
}
///|
fn Output::begin_capture(self : Output, mode : CaptureMode) -> Unit {
self.capture_stack.push(
match mode {
Capture => Some(StringBuilder())
Discard => None
},
)
}
///|
fn Output::end_capture(self : Output, auto_escape : AutoEscape) -> Value {
match self.capture_stack.pop() {
Some(Some(captured)) =>
if auto_escape is NoEscape {
Value::from_string(captured.to_string())
} else {
Value::from_safe_string(captured.to_string())
}
_ => Value::undefined()
}
}
///|
fn Output::is_discarding(self : Output) -> Bool {
self.capture_stack.last() is Some(None)
}
///|
/// Writes a string to the output.
pub fn Output::write_str(
self : Output,
s : StringView,
) -> Unit raise TemplateError {
match self.capture_stack.last() {
Some(Some(sb)) => sb.write_view(s)
Some(None) => ()
None => self.write_to_root(s)
}
}
///|
/// Writes a character to the output.
pub fn Output::write_char(self : Output, c : Char) -> Unit raise TemplateError {
match self.capture_stack.last() {
Some(Some(sb)) => sb.write_char(c)
Some(None) => ()
None => self.write_to_root(c.to_string())
}
}