-
Notifications
You must be signed in to change notification settings - Fork 3
/
json.gleam
122 lines (110 loc) · 3.35 KB
/
json.gleam
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import gleam/int
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/json.{type Json}
import sprocket/internal/reconcile.{
type ReconciledAttribute, type ReconciledElement, ReconciledAttribute,
ReconciledClientHook, ReconciledComponent, ReconciledCustom, ReconciledElement,
ReconciledEventHandler, ReconciledFragment, ReconciledIgnoreUpdate,
ReconciledText,
}
import sprocket/render.{type Renderer, Renderer}
pub fn json_renderer() -> Renderer(Json) {
Renderer(render: fn(el: ReconciledElement) { render(el) })
}
fn render(el: ReconciledElement) -> Json {
case el {
ReconciledElement(tag: tag, key: key, attrs: attrs, children: children) ->
element(tag, key, attrs, children)
ReconciledComponent(key: key, el: el, ..) -> component(key, el)
ReconciledFragment(key, children: children) -> fragment(key, children)
ReconciledIgnoreUpdate(el) -> render(el)
ReconciledText(text: t) -> text(t)
ReconciledCustom(kind: kind, data: data) -> custom(kind, data)
}
}
fn element(
tag: String,
key: Option(String),
attrs: List(ReconciledAttribute),
children: List(ReconciledElement),
) -> Json {
let #(attrs, events, hooks) =
attrs
|> list.fold(#([], [], []), fn(acc, attr) {
let #(attrs, events, hooks) = acc
case attr {
ReconciledAttribute(name, value) -> {
#([#(name, json.string(value)), ..attrs], events, hooks)
}
ReconciledEventHandler(kind, id) -> {
#(
attrs,
[
[#("kind", json.string(kind)), #("id", json.string(id))]
|> json.object(),
..events
],
hooks,
)
}
ReconciledClientHook(name, id) -> {
#(attrs, events, [
[#("name", json.string(name)), #("id", json.string(id))]
|> json.object(),
..hooks
])
}
}
})
let children =
children
|> list.index_map(fn(child, i) { #(int.to_string(i), render(child)) })
[
#("type", json.string("element")),
#("tag", json.string(tag)),
#("attrs", json.object(attrs)),
#("events", json.preprocessed_array(events)),
#("hooks", json.preprocessed_array(hooks)),
]
|> maybe_append_string("key", key)
|> list.append(children)
|> json.object()
}
fn component(key: Option(String), el: ReconciledElement) -> Json {
[#("type", json.string("component"))]
|> maybe_append_string("key", key)
|> list.append([#("0", render(el))])
|> json.object()
}
fn fragment(key: Option(String), children: List(ReconciledElement)) -> Json {
let children =
children
|> list.index_map(fn(child, i) { #(int.to_string(i), render(child)) })
[#("type", json.string("fragment"))]
|> maybe_append_string("key", key)
|> list.append(children)
|> json.object()
}
fn text(t: String) -> Json {
json.string(t)
}
fn custom(kind: String, data: String) -> Json {
[
#("type", json.string("custom")),
#("kind", json.string(kind)),
#("data", json.string(data)),
]
|> json.object()
}
// appends a string property to a json object if the value is present
fn maybe_append_string(
json_object_builder: List(#(String, Json)),
key: String,
value: Option(String),
) -> List(#(String, Json)) {
case value {
Some(v) -> list.append(json_object_builder, [#(key, json.string(v))])
None -> json_object_builder
}
}