Skip to content
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 Writer.embed function to allow writing pre-formed XML without escaping anything #39

Merged
merged 3 commits into from
Oct 26, 2024

Conversation

MFAshby
Copy link
Contributor

@MFAshby MFAshby commented Oct 25, 2024

Useful for embedding one XML fragment into another

escaping anything

Useful for embedding one XML fragment into another
Copy link
Owner

@ianprime0509 ianprime0509 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! I think this is a good function to have. I have a couple suggestions to expand the possible use-cases for it. Also, it should be added to GenericWriter so users of that API can call it:

zig-xml/src/xml.zig

Lines 433 to 471 in ee8e09b

pub fn GenericWriter(comptime SinkError: type) type {
return struct {
writer: Writer,
pub const WriteError = Writer.WriteError || SinkError;
pub inline fn bom(writer: *@This()) WriteError!void {
return @errorCast(writer.writer.bom());
}
pub inline fn xmlDeclaration(writer: *@This(), encoding: ?[]const u8, standalone: ?bool) WriteError!void {
return @errorCast(writer.writer.xmlDeclaration(encoding, standalone));
}
pub inline fn elementStart(writer: *@This(), name: []const u8) WriteError!void {
return @errorCast(writer.writer.elementStart(name));
}
pub inline fn elementEnd(writer: *@This(), name: []const u8) WriteError!void {
return @errorCast(writer.writer.elementEnd(name));
}
pub inline fn elementEndEmpty(writer: *@This()) WriteError!void {
return @errorCast(writer.writer.elementEndEmpty());
}
pub inline fn attribute(writer: *@This(), name: []const u8, value: []const u8) WriteError!void {
return @errorCast(writer.writer.attribute(name, value));
}
pub inline fn pi(writer: *@This(), target: []const u8, data: []const u8) WriteError!void {
return @errorCast(writer.writer.pi(target, data));
}
pub inline fn text(writer: *@This(), s: []const u8) WriteError!void {
return @errorCast(writer.writer.text(s));
}
};
}

src/Writer.zig Outdated
Comment on lines 188 to 192
switch (writer.state) {
.after_structure_end, .text => {},
.element_start => try writer.raw(">"),
.start, .after_bom, .after_xml_declaration, .end => unreachable,
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
switch (writer.state) {
.after_structure_end, .text => {},
.element_start => try writer.raw(">"),
.start, .after_bom, .after_xml_declaration, .end => unreachable,
}
switch (writer.state) {
.start, .after_bom, .after_xml_declaration, .after_structure_end, .text, .end => {},
.element_start => try writer.raw(">"),
}

I don't see any reason to restrict this function to operate only inside the root element: it's reasonable that someone might want to use it to embed a DTD at the beginning of the document, for example, which isn't currently possible with the other methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point, I was thinking too narrowly about my own use case, embedding xml fragments inside an existing document. Fixed in f54c606

src/Writer.zig Outdated
.start, .after_bom, .after_xml_declaration, .end => unreachable,
}
try writer.raw(s);
writer.state = .after_structure_end;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
writer.state = .after_structure_end;
writer.state = switch (writer.state) {
.start, .after_bom, .after_xml_declaration => .after_xml_declaration,
.element_start, .after_structure_end, .text => .text,
.end => .end,
};

Once this function is opened up to work in more scenarios, other possible states also need to be handled. In this suggestion, I chose text as the ending state within the root element to avoid indentation if another element is started right after this function (to maximize the possible uses, including for embedding raw text nodes).

@MFAshby
Copy link
Contributor Author

MFAshby commented Oct 26, 2024

@ianprime0509 thanks for the review, I'm glad this can be useful. I'll apply your suggestions shortly!

Fixes: ianprime0509#39 (review)

Make the "embed" function more permissive about contexts in which it's
used, as this makes it more flexible

Fixes:
- ianprime0509#39 (comment)
- ianprime0509#39 (comment)
Add a 'testbed' facility for writing more test.
@MFAshby
Copy link
Contributor Author

MFAshby commented Oct 26, 2024

Thanks again @ianprime0509 I've added your suggestions in f54c606

I've also started adding tests for Writer as there didn't seem to be any 1a0355a, but happy to drop this commit or move it to a new PR if it's not something you're interested in right now.

@ianprime0509
Copy link
Owner

Awesome, thanks! Yeah, I haven't added any tests or proper doc comments to the writer yet, unfortunately. I'll probably go a slightly different direction with the tests when I get to it (focusing on having one self-contained doctest for each function, similar to the current reader tests), but I think what you have for now is a useful start.

@ianprime0509 ianprime0509 merged commit 93c3444 into ianprime0509:main Oct 26, 2024
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants