Skip to content

Commit

Permalink
feat: add writer functions for remaining node types
Browse files Browse the repository at this point in the history
  • Loading branch information
ianprime0509 committed Jan 3, 2025
1 parent cad4e37 commit c33f9f3
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
128 changes: 128 additions & 0 deletions src/Writer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,44 @@ fn attributeText(writer: *Writer, s: []const u8) anyerror!void {
try writer.write(s[pos..]);
}

/// Writes a comment.
pub fn comment(writer: *Writer, s: []const u8) anyerror!void {
switch (writer.state) {
.start, .after_bom, .after_xml_declaration, .text, .end => {},
.element_start => {
try writer.write(">");
try writer.newLineAndIndent();
},
.after_structure_end => {
try writer.newLineAndIndent();
},
}
try writer.write("<!--");
try writer.write(s);
try writer.write("-->");
writer.state = .after_structure_end;
}

test comment {
var raw = std.ArrayList(u8).init(std.testing.allocator);
defer raw.deinit();
const out = xml.streamingOutput(raw.writer());
var writer = out.writer(std.testing.allocator, .{ .indent = " " });
defer writer.deinit();

try writer.comment(" Here is the document: ");
try writer.elementStart("root");
try writer.comment(" I am inside the document ");
try writer.elementEnd();

try expectEqualStrings(
\\<!-- Here is the document: -->
\\<root>
\\ <!-- I am inside the document -->
\\</root>
, raw.items);
}

/// Writes a PI (processing instruction).
pub fn pi(writer: *Writer, target: []const u8, data: []const u8) anyerror!void {
switch (writer.state) {
Expand Down Expand Up @@ -612,6 +650,96 @@ test text {
, raw.items);
}

/// Writes a CDATA node.
/// Asserts that the writer is in an element.
pub fn cdata(writer: *Writer, s: []const u8) anyerror!void {
switch (writer.state) {
.after_structure_end, .text => {},
.element_start => try writer.write(">"),
.start, .after_bom, .after_xml_declaration, .end => unreachable,
}
try writer.write("<![CDATA[");
try writer.write(s);
try writer.write("]]>");
writer.state = .text;
}

test cdata {
var raw = std.ArrayList(u8).init(std.testing.allocator);
defer raw.deinit();
const out = xml.streamingOutput(raw.writer());
var writer = out.writer(std.testing.allocator, .{ .indent = " " });
defer writer.deinit();

try writer.elementStart("root");
try writer.cdata("Look, no <escaping> needed!");
try writer.elementEnd();

try expectEqualStrings(
\\<root><![CDATA[Look, no <escaping> needed!]]></root>
, raw.items);
}

/// Writes a character reference.
/// Asserts that the writer is in an element.
pub fn characterReference(writer: *Writer, c: u21) anyerror!void {
switch (writer.state) {
.after_structure_end, .text => {},
.element_start => try writer.write(">"),
.start, .after_bom, .after_xml_declaration, .end => unreachable,
}
const fmt = "&#x{X};";
var buf: [std.fmt.count(fmt, .{std.math.maxInt(u21)})]u8 = undefined;
try writer.write(std.fmt.bufPrint(&buf, fmt, .{c}) catch unreachable);
writer.state = .text;
}

test characterReference {
var raw = std.ArrayList(u8).init(std.testing.allocator);
defer raw.deinit();
const out = xml.streamingOutput(raw.writer());
var writer = out.writer(std.testing.allocator, .{ .indent = " " });
defer writer.deinit();

try writer.elementStart("root");
try writer.characterReference('龍');
try writer.elementEnd();

try expectEqualStrings(
\\<root>&#x9F8D;</root>
, raw.items);
}

/// Writes an entity reference.
/// Asserts that the writer is in an element.
pub fn entityReference(writer: *Writer, name: []const u8) anyerror!void {
switch (writer.state) {
.after_structure_end, .text => {},
.element_start => try writer.write(">"),
.start, .after_bom, .after_xml_declaration, .end => unreachable,
}
try writer.write("&");
try writer.write(name);
try writer.write(";");
writer.state = .text;
}

test entityReference {
var raw = std.ArrayList(u8).init(std.testing.allocator);
defer raw.deinit();
const out = xml.streamingOutput(raw.writer());
var writer = out.writer(std.testing.allocator, .{ .indent = " " });
defer writer.deinit();

try writer.elementStart("root");
try writer.entityReference("amp");
try writer.elementEnd();

try expectEqualStrings(
\\<root>&amp;</root>
, raw.items);
}

/// Writes an XML fragment without escaping anything.
///
/// For correctness, the XML fragment must not contain any unclosed structures.
Expand Down
20 changes: 20 additions & 0 deletions src/xml.zig
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,11 @@ pub fn GenericWriter(comptime SinkError: type) type {
return @errorCast(writer.writer.attributeNs(ns, local, value));
}

/// See `Writer.comment`.
pub inline fn comment(writer: *@This(), s: []const u8) WriteError!void {
return @errorCast(writer.writer.comment(s));
}

/// See `Writer.pi`.
pub inline fn pi(writer: *@This(), target: []const u8, data: []const u8) WriteError!void {
return @errorCast(writer.writer.pi(target, data));
Expand All @@ -498,6 +503,21 @@ pub fn GenericWriter(comptime SinkError: type) type {
return @errorCast(writer.writer.text(s));
}

/// See `Writer.cdata`.
pub inline fn cdata(writer: *@This(), s: []const u8) WriteError!void {
return @errorCast(writer.writer.cdata(s));
}

/// See `Writer.characterReference`.
pub inline fn characterReference(writer: *@This(), c: u21) WriteError!void {
return @errorCast(writer.writer.characterReference(c));
}

/// See `Writer.entityReference`.
pub inline fn entityReference(writer: *@This(), name: []const u8) WriteError!void {
return @errorCast(writer.writer.entityReference(name));
}

/// See `Writer.embed`.
pub inline fn embed(writer: *@This(), s: []const u8) WriteError!void {
return @errorCast(writer.writer.embed(s));
Expand Down

0 comments on commit c33f9f3

Please sign in to comment.