Ok, to push the pawn, here’s a ridiculous nascent protohack implementing a specific use case. I’ll tell you why if you make it to the end.
pub const T = enum { html, head, title, body, div, span, }; // ...
pub fn render(tree: anytype) void {
var close = false;
// tag:
if (@hasField(@TypeOf(tree), "tag")) {
std.debug.print("<" ++ @tagName(@field(tree, "tag")), .{});
close = true;
}
// attrs:
if (@hasField(@TypeOf(tree), "attrs")) {
const attrs = @field(tree, "attrs");
if (@typeInfo(@TypeOf(attrs)) != .@"struct") unreachable;
inline for (std.meta.fields(@TypeOf(attrs))) |attr| {
std.debug.print(" {s} = {s}", .{attr.name, @field(attrs, attr.name)});
}
}
// alternately, embedded trees:
inline for (std.meta.fields(@TypeOf(tree))) |field| {
const tup: ?usize = std.fmt.parseInt(usize, field.name, 10) catch null;
if (tup != null) {
const content = @field(tree, field.name);
if (@typeInfo(@TypeOf(content)) == .@"struct") { // incl. tuples!
render(content);
}
}
}
// content:
if (@hasField(@TypeOf(tree), "content")) {
std.debug.print(">\n", .{});
const content = @field(tree, "content");
if (@typeInfo(@TypeOf(content)) == .@"struct") { // incl. tuples!
render(content);
} else { // TODO: validate that it's a []const u8
std.debug.print(content ++ "\n", .{});
}
std.debug.print("</" ++ @tagName(@field(tree, "tag")) ++ ">\n", .{});
} else if (close) {
std.debug.print(" />", .{});
}
}
With goofy debug-prints, it spits HTML (without any containment validation, etc.) given something like this:
test "html" {
const doc = .{ .tag = T.html, .content = .{ .{ .tag =
T.head, .content = .{ .{ .tag =
T.title, .content =
"A bitty HTML sample"
} },
}, .{ .tag =
T.body, .content = .{ .{ .tag =
T.div, .attrs = .{ .id = "crack", .class = "warnings", }, .content =
"text in first div"
}, .{ .tag =
T.div, .content =
"text in second div"
}, .{ .tag =
T.div, .content = .{ .{ .tag =
T.span, .content =
"text in span"
} },
} },
} },
};
render(doc);
}
Why? I think this is pretty useless (to me) by itself. But ages ago I decided I didn’t like templating languages (or the motif at all, really), and I stumbled upon dominate, which was much to my liking, and for years I wrote python whose “template” layer was just python code. This is most valuable for the dynamic cases (building the structure from some data, e.g.), and, in my case, 90% of the HTML was NOT boilerplate, so using a real language was a joy for me - far superior to making for-loops in a template language. But, of course there’s always some static stuff, too, and it was nice that dominate made it easy to do, often in-line, to make things pretty readable.
In my opinion, if I were to take on this pet project idea, dominate could be an inspiration, but the end result would probably not bear as much resemblance as anticipated, due to language differences. But who knows, maybe a creative turn will lead to something even nicer.
Anyway, the hack above is just a proof-of-concept for the static case. I’m more interested in working on the dynamic tree building, but I wanted to be sure I could do something like this to scaffold the static/boilerplate stuff.
Why the message? As usual, I’d love critique, in the form of: “what an awful/awesome idea”, and in the form of: “Jonny already did that, look here”, and in the form of: “you’re doing it wrong - do this instead”. Thanks.