aboutsummaryrefslogtreecommitdiff
path: root/src/html/Element.zig
blob: 8ecf31ecdadb56d50056e3a22797d30d4ea99495 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
const Self = @This();

pub const Content = union(enum) {
    elements: []const Self,
    string: []const u8,
    void: void,

    pub fn content(string: []const u8) @This() {
        return .{ .string = string };
    }

    pub fn children(elements: []const Self) @This() {
        return .{ .elements = elements };
    }
};

pub const Attribute = struct {
    name: []const u8,
    value: []const u8,
};

tag: []const u8,
attributes: []const Attribute,
content: Content,

fn attributesFrom(attributes: anytype) []const Attribute {
    const T = @TypeOf(attributes);
    const info = @typeInfo(T);

    if (info != .@"struct") {
        @compileError("struct needed");
    }

    const fields = info.@"struct".fields;

    var attrs: []const Attribute = &.{};

    for (fields) |field| {
        const name = field.name;
        const value = @field(attributes, name);

        const next_attr: []const Attribute = &.{ .{ .name = name, .value = value } };
        attrs = attrs ++ next_attr;
    }

    return attrs;
}

pub fn native(name: @EnumLiteral(), attrs: anytype, content: Content) Self {
    return .{
        .tag = @tagName(name),
        .attributes = attributesFrom(attrs),
        .content = content,
    };
}

pub fn transparent(content: Content) Self {
    return .{
        .tag = "",
        .attributes = &.{},
        .content = content,
    };
}

pub fn toHtml(comptime self: Self) []const u8 {
    var string: []const u8 = string: {
        if (self.tag.len == 0) {
            break :string "";
        }

        var string: []const u8 = "<" ++ self.tag;

        inline for (self.attributes) |attr| {
            string = string ++ " " ++ attr.name ++ "='" ++ attr.value ++ "'";
        }

        string = string ++ ">";

        break :string string;
    };


    switch (self.content) {
        .void => {},
        .string => |s| {
            string = string ++ s ++ "</" ++ self.tag ++ ">";
        },
        .elements => |els| {
            for (els) |el| {
                string = string ++ el.toHtml();
            }

            string = string ++ "</" ++ self.tag ++ ">";
        }
    }

    string = string ++ if (self.tag.len == 0) "" else "</" ++ self.tag ++ ">";

    return string;
}

pub fn toDocument(comptime self: Self) []const u8 {
    return "<!DOCTYPE html>" ++ self.toHtml();
}