///|
/// A bounded batch expansion failed before returning a partial result.
pub(all) enum BatchError {
TooManyInputs
TotalOutputTooLong
InvalidItem(Int, ExpandError)
}
///|
pub fn BatchError::code(self : BatchError) -> String {
match self {
TooManyInputs => "too_many_inputs"
TotalOutputTooLong => "total_output_too_long"
InvalidItem(_, _) => "invalid_item"
}
}
///|
/// Return the zero-based row number for a per-row expansion failure.
pub fn BatchError::index(self : BatchError) -> Int? {
match self {
InvalidItem(index, _) => Some(index)
_ => None
}
}
///|
/// Expand one compiled template for many independent value sets. The call is
/// fail-fast and atomic: no partial result escapes on error. An aggregate
/// output limit prevents an unbounded result even when each row is small.
pub fn Template::expand_many(
self : Template,
inputs : Array[Array[(String, Value)]],
max_inputs? : Int = 4096,
max_total_output? : Int = 16777216,
max_each_output? : Int = 1048576,
) -> Result[Array[String], BatchError] {
if max_inputs < 0 || inputs.length() > max_inputs {
return Err(TooManyInputs)
}
if max_total_output < 0 || max_each_output < 0 {
return Err(TotalOutputTooLong)
}
let outputs : Array[String] = []
let mut used = 0
for index, bindings in inputs {
let output = match self.expand(bindings, max_output=max_each_output) {
Ok(value) => value
Err(error) => return Err(InvalidItem(index, error))
}
if output.length() > max_total_output - used {
return Err(TotalOutputTooLong)
}
used += output.length()
outputs.push(output)
}
Ok(outputs)
}