aboutsummaryrefslogtreecommitdiff
path: root/src/eval/html/html.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval/html/html.zig')
-rw-r--r--src/eval/html/html.zig47
1 files changed, 47 insertions, 0 deletions
diff --git a/src/eval/html/html.zig b/src/eval/html/html.zig
new file mode 100644
index 0000000..064ebb1
--- /dev/null
+++ b/src/eval/html/html.zig
@@ -0,0 +1,47 @@
+const std = @import("std");
+const eql = std.mem.eql;
+
+pub const Error = error{InvalidUtf8} || std.mem.Allocator.Error;
+
+pub fn escape(alloc: std.mem.Allocator, v: []const u8) Error![]const u8 {
+ var acc = try std.ArrayList(u8).initCapacity(alloc, v.len);
+ errdefer acc.deinit(alloc);
+ const view = try std.unicode.Utf8View.init(v);
+ var iter = view.iterator();
+ while (iter.nextCodepointSlice()) |rune| {
+ if (eql(u8, rune, "&")) {
+ try acc.appendSlice(alloc, "&");
+ } else if (eql(u8, rune, "'")) {
+ try acc.appendSlice(alloc, "'");
+ } else if (eql(u8, rune, "<")) {
+ try acc.appendSlice(alloc, "&lt;");
+ } else if (eql(u8, rune, ">")) {
+ try acc.appendSlice(alloc, "&gt;");
+ } else if (eql(u8, rune, "\"")) {
+ try acc.appendSlice(alloc, "&#34;");
+ } else {
+ try acc.appendSlice(alloc, rune);
+ }
+ }
+ return acc.toOwnedSlice(alloc);
+}
+
+fn doTest(alloc: std.mem.Allocator, el: []const u8, exp: []const u8) !void {
+ const got = try escape(alloc, el);
+ defer alloc.free(got);
+ std.testing.expect(eql(u8, got, exp)) catch |err| {
+ std.debug.print("{s}\n", .{got});
+ return err;
+ };
+}
+
+test "escaping html" {
+ const alloc = std.testing.allocator;
+
+ try doTest(alloc, "hello world", "hello world");
+ try doTest(alloc, "hello&world", "hello&amp;world");
+ try doTest(alloc, "hello'world", "hello&#39;world");
+ try doTest(alloc, "hello<world", "hello&lt;world");
+ try doTest(alloc, "hello>world", "hello&gt;world");
+ try doTest(alloc, "hello\"world", "hello&#34;world");
+}