///|
/// A bounded event writer. Conditions and root directives require a document
/// resolver and are rejected rather than silently changing their meaning.
struct StreamWriter {
sink : (String) -> Unit raise
mut depth : Int
mut closed : Bool
}
///|
pub fn new_stream_writer(sink : (String) -> Unit raise) -> StreamWriter {
{ sink, depth: 0, closed: false, }
}
///|
pub fn StreamWriter::write(
self : StreamWriter,
event : StreamEvent,
) -> Unit raise {
if self.closed {
raise InvalidDocument("Stream writer is closed")
}
errdefer {
self.closed = true
}
let builder = StringBuilder()
match event {
Pair(key, _) | BeginObject(key) => {
if self.depth == 0 &&
(
key.equal_ignore_ascii_case("#include") ||
key.equal_ignore_ascii_case("#base")
) {
raise InvalidDocument(
"Resolve root directives before stream formatting",
)
}
write_indent(builder, self.depth)
write_quoted(builder, key)
match event {
Pair(_, value) => {
builder.write_string(" ")
write_quoted(builder, value)
builder.write_string("\n")
}
_ => ()
}
}
EndObject => ()
Condition(_, _) =>
raise InvalidDocument(
"Conditions require document evaluation before formatting",
)
}
match event {
BeginObject(_) => {
if self.depth >= 128 {
raise InvalidDocument("Stream writer exceeds depth 128")
}
builder.write_string("\n")
write_indent(builder, self.depth)
builder.write_string("{\n")
self.depth += 1
}
EndObject => {
if self.depth == 0 {
raise InvalidDocument("Unmatched stream end")
}
self.depth -= 1
write_indent(builder, self.depth)
builder.write_string("}\n")
}
_ => ()
}
(self.sink)(builder.to_string())
}
///|
pub fn StreamWriter::finish(self : StreamWriter) -> Unit raise WriteError {
if self.closed {
raise InvalidDocument("Stream writer is closed")
}
self.closed = true
if self.depth != 0 {
raise InvalidDocument("Unclosed stream object")
}
}