mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 11:23:32 -04:00
feat: Added mustache templating.
This commit is contained in:
@@ -0,0 +1,960 @@
|
||||
#+feature dynamic-literals
|
||||
package mustache
|
||||
|
||||
import "core:encoding/json"
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:reflect"
|
||||
import "core:slice"
|
||||
import "core:testing"
|
||||
|
||||
COMMENTS_SPEC :: "mustache/spec/specs/comments.json"
|
||||
DELIMITERS_SPEC :: "mustache/spec/specs/delimiters.json"
|
||||
INTERPOLATION_SPEC :: "mustache/spec/specs/interpolation.json"
|
||||
INVERTED_SPEC :: "mustache/spec/specs/inverted.json"
|
||||
PARTIALS_SPEC :: "mustache/spec/specs/partials.json"
|
||||
SECTIONS_SPEC :: "mustache/spec/specs/sections.json"
|
||||
|
||||
Test_Struct :: struct {
|
||||
name: string,
|
||||
email: string,
|
||||
}
|
||||
Test_Map :: map[string]string
|
||||
Test_List :: [dynamic]string
|
||||
Test_Data :: union {
|
||||
Test_Struct,
|
||||
Test_Map,
|
||||
Test_List,
|
||||
string,
|
||||
}
|
||||
|
||||
// 1. A map from string => JSON_Data
|
||||
// 2. A list of JSON_Data
|
||||
// 3. A value of some kind (string, int, etc.)
|
||||
JSON_Map :: distinct map[string]JSON_Data
|
||||
JSON_List :: distinct [dynamic]JSON_Data
|
||||
JSON_Data :: union {
|
||||
JSON_Map,
|
||||
JSON_List,
|
||||
string,
|
||||
}
|
||||
|
||||
load_json :: proc(val: json.Value) -> (loaded: JSON_Data) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
switch _val in val {
|
||||
case bool, string:
|
||||
v := fmt.tprintf("%v", _val)
|
||||
loaded = v
|
||||
case i64, f64:
|
||||
str := fmt.tprintf("%.2f", val)
|
||||
decimal_str := trim_decimal_string(str)
|
||||
loaded = decimal_str
|
||||
case json.Object:
|
||||
data := make(JSON_Map)
|
||||
for k, v in _val {
|
||||
new_k := fmt.tprintf("%v", k)
|
||||
data[new_k] = load_json(v)
|
||||
}
|
||||
loaded = data
|
||||
case json.Array:
|
||||
data := make(JSON_List)
|
||||
for v in _val {
|
||||
append(&data, load_json(v))
|
||||
}
|
||||
loaded = data
|
||||
case json.Null:
|
||||
}
|
||||
|
||||
return loaded
|
||||
}
|
||||
|
||||
load_spec :: proc(filename: string) -> (json.Value) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
data, read_err := os.read_entire_file_from_path(filename, context.allocator)
|
||||
if read_err != nil {
|
||||
fmt.println("Failed to load the file!")
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
json_data, err := json.parse(data)
|
||||
if err != .None {
|
||||
fmt.println("Failed to parse the .json file")
|
||||
fmt.println("Error:", err)
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
return json_data
|
||||
}
|
||||
|
||||
assert :: proc(
|
||||
t: ^testing.T,
|
||||
actual: bool,
|
||||
msg: string,
|
||||
exp := #caller_expression(actual),
|
||||
loc := #caller_location,
|
||||
) {
|
||||
testing.expect(t, actual, msg, exp, loc)
|
||||
}
|
||||
|
||||
assert_not :: proc(
|
||||
t: ^testing.T,
|
||||
actual: bool,
|
||||
msg: string,
|
||||
exp := #caller_expression(actual),
|
||||
loc := #caller_location,
|
||||
) {
|
||||
testing.expect(t, !actual, msg, exp, loc)
|
||||
}
|
||||
|
||||
assert_mustache :: proc(
|
||||
t: ^testing.T,
|
||||
input: string,
|
||||
data: any,
|
||||
exp_output: string,
|
||||
partials: any = map[string]string{},
|
||||
loc := #caller_location,
|
||||
) {
|
||||
output, _ := render(input, data, partials)
|
||||
testing.expect_value(t, output, exp_output, loc)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{x}}, nice to meet you. My name is {{y}}."
|
||||
|
||||
data := make(Test_Map, 2)
|
||||
data["x"] = "Vincent"
|
||||
data["y"] = "R2D2"
|
||||
|
||||
exp_output := "Hello, Vincent, nice to meet you. My name is R2D2."
|
||||
output, _ := render(template, data)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_in_layout :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{x}}, nice to meet you. My name is {{y}}."
|
||||
|
||||
data := make(Test_Map, 2)
|
||||
data["x"] = "Vincent"
|
||||
data["y"] = "R2D2"
|
||||
|
||||
layout := "\nAbove.\n{{content}}\nBelow."
|
||||
|
||||
exp_output := "\nAbove.\nHello, Vincent, nice to meet you. My name is R2D2.\nBelow."
|
||||
output, _ := render_in_layout(template, data, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_in_layout_with_data_for_layout :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello"
|
||||
|
||||
data := make(Test_Map, 1, context.temp_allocator)
|
||||
data["x"] = "42"
|
||||
|
||||
layout := "\n{{x}}\n{{content}}\n"
|
||||
|
||||
exp_output := "\n42\nHello\n"
|
||||
output, _ := render_in_layout(template, data, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_in_layout_file :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{name}}."
|
||||
data := make(Test_Map, 1, context.temp_allocator)
|
||||
data["name"] = "Vincent"
|
||||
layout := "mustache/test/layout.txt"
|
||||
|
||||
exp_output := "Begin layout >>\nHello, Vincent.\n<< End layout\n"
|
||||
output, _ := render_in_layout_file(template, data, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_from_filename :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "mustache/test/template.txt"
|
||||
data := make(Test_Map, 1, context.temp_allocator)
|
||||
data["name"] = "Vincent"
|
||||
|
||||
exp_output := "Hello, this is Vincent.\n"
|
||||
output, _ := render_from_filename(template, data)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_from_filename_in_layout :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "mustache/test/template.txt"
|
||||
data := make(Test_Map, 1, context.temp_allocator)
|
||||
data["name"] = "Vincent"
|
||||
layout := "\nAbove.\n{{content}}\nBelow."
|
||||
|
||||
exp_output := "\nAbove.\nHello, this is Vincent.\nBelow."
|
||||
output, _ := render_from_filename_in_layout(template, data, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_from_filename_in_layout_file :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "mustache/test/template.txt"
|
||||
data := make(Test_Map, 1, context.temp_allocator)
|
||||
data["name"] = "Vincent"
|
||||
layout := "mustache/test/layout.txt"
|
||||
|
||||
exp_output := "Begin layout >>\nHello, this is Vincent.\n<< End layout\n"
|
||||
output, _ := render_from_filename_in_layout_file(template, data, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_with_json :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{name}}."
|
||||
json := "mustache/test/data.json"
|
||||
|
||||
exp_output := "Hello, Kilgarvan."
|
||||
output, _ := render_with_json(template, json)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
defer(delete(output))
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_with_json_in_layout :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{name}}."
|
||||
json := "mustache/test/data.json"
|
||||
layout := "\nAbove.\n{{content}}\nBelow."
|
||||
|
||||
exp_output := "\nAbove.\nHello, Kilgarvan.\nBelow."
|
||||
output, _ := render_with_json_in_layout(template, json, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_with_json_in_layout_file :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{name}}."
|
||||
json := "mustache/test/data.json"
|
||||
layout := "mustache/test/layout.txt"
|
||||
|
||||
exp_output := "Begin layout >>\nHello, Kilgarvan.\n<< End layout\n"
|
||||
output, _ := render_with_json_in_layout_file(template, json, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_from_filename_with_json_in_layout :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "mustache/test/template.txt"
|
||||
json := "mustache/test/data.json"
|
||||
layout := "\nAbove.\n{{content}}\nBelow."
|
||||
|
||||
exp_output := "\nAbove.\nHello, this is Kilgarvan.\nBelow."
|
||||
output, _ := render_from_filename_with_json_in_layout(template, json, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_render_from_filename_with_json_in_layout_file :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "mustache/test/template.txt"
|
||||
json := "mustache/test/data.json"
|
||||
layout := "mustache/test/layout.txt"
|
||||
|
||||
exp_output := "Begin layout >>\nHello, this is Kilgarvan.\n<< End layout\n"
|
||||
output, _ := render_from_filename_with_json_in_layout_file(template, json, layout)
|
||||
testing.expect_value(t, output, exp_output)
|
||||
|
||||
delete(output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_struct :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{name}}. Send an email to {{email}}."
|
||||
data := Test_Struct {"Vincent", "foo@example.com"}
|
||||
exp_output := "Hello, Vincent. Send an email to foo@example.com."
|
||||
assert_mustache(t, template, data, exp_output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_struct_union :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{name}}. Send an email to {{email}}."
|
||||
data: Test_Data
|
||||
data = Test_Struct {"Vincent", "foo@example.com"}
|
||||
exp_output := "Hello, Vincent. Send an email to foo@example.com."
|
||||
assert_mustache(t, template, data, exp_output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_struct_inside_map :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{name}}. Send an email to {{#email}}{{address}}{{/email}}."
|
||||
|
||||
data := make(map[string]Test_Data, 2, context.temp_allocator)
|
||||
data["name"] = "Vincent"
|
||||
data["email"] = make(Test_Map, 1, context.temp_allocator)
|
||||
email := data["email"].(Test_Map)
|
||||
email["address"] = "foo@example.com"
|
||||
data["email"] = email
|
||||
|
||||
exp_output := "Hello, Vincent. Send an email to foo@example.com."
|
||||
assert_mustache(t, template, data, exp_output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_list :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "{{#names}}{{.}}{{/names}}"
|
||||
|
||||
data := make(map[string][dynamic]string, 1, context.temp_allocator)
|
||||
names := make([dynamic]string, 2, context.temp_allocator)
|
||||
append(&names, "Helena", " Bloomington")
|
||||
data["names"] = names
|
||||
|
||||
exp_output := "Helena Bloomington"
|
||||
assert_mustache(t, template, data, exp_output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_no_interpolation :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {Mustache}!"
|
||||
data := ""
|
||||
exp_output := "Hello, {Mustache}!"
|
||||
assert_mustache(t, template, data, exp_output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_literal_tag :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
template := "Hello, {{{verb1}}}."
|
||||
data := Test_Map {
|
||||
"verb1" = "I like < >",
|
||||
}
|
||||
defer(delete(data))
|
||||
|
||||
exp_output := "Hello, I like < >."
|
||||
assert_mustache(t, template, data, exp_output)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_interpolation_spec :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
spec := load_spec(INTERPOLATION_SPEC)
|
||||
defer json.destroy_value(spec)
|
||||
|
||||
root := spec.(json.Object)
|
||||
tests := root["tests"].(json.Array)
|
||||
|
||||
for test in tests {
|
||||
test_obj := test.(json.Object)
|
||||
template := test_obj["template"].(string)
|
||||
exp_output := test_obj["expected"].(string)
|
||||
data := test_obj["data"]
|
||||
input := load_json(data)
|
||||
|
||||
assert_mustache(t, template, input, exp_output)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_comments_spec :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
spec := load_spec(COMMENTS_SPEC)
|
||||
defer json.destroy_value(spec)
|
||||
|
||||
root := spec.(json.Object)
|
||||
tests := root["tests"].(json.Array)
|
||||
|
||||
for test in tests {
|
||||
test_obj := test.(json.Object)
|
||||
template := test_obj["template"].(string)
|
||||
exp_output := test_obj["expected"].(string)
|
||||
data := test_obj["data"]
|
||||
input := load_json(data)
|
||||
|
||||
assert_mustache(t, template, input, exp_output)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_sections_spec :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
spec := load_spec(SECTIONS_SPEC)
|
||||
defer json.destroy_value(spec)
|
||||
|
||||
root := spec.(json.Object)
|
||||
tests := root["tests"].(json.Array)
|
||||
|
||||
for test in tests {
|
||||
test_obj := test.(json.Object)
|
||||
template := test_obj["template"].(string)
|
||||
exp_output := test_obj["expected"].(string)
|
||||
data := test_obj["data"]
|
||||
input := load_json(data)
|
||||
|
||||
assert_mustache(t, template, input, exp_output)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_inverted_spec :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
spec := load_spec(INVERTED_SPEC)
|
||||
defer json.destroy_value(spec)
|
||||
|
||||
root := spec.(json.Object)
|
||||
tests := root["tests"].(json.Array)
|
||||
|
||||
for test in tests {
|
||||
test_obj := test.(json.Object)
|
||||
template := test_obj["template"].(string)
|
||||
exp_output := test_obj["expected"].(string)
|
||||
data := test_obj["data"]
|
||||
input := load_json(data)
|
||||
|
||||
assert_mustache(t, template, input, exp_output)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_partials_spec :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
spec := load_spec(PARTIALS_SPEC)
|
||||
defer json.destroy_value(spec)
|
||||
|
||||
root := spec.(json.Object)
|
||||
tests := root["tests"].(json.Array)
|
||||
|
||||
for test in tests {
|
||||
test_obj := test.(json.Object)
|
||||
template := test_obj["template"].(string)
|
||||
exp_output := test_obj["expected"].(string)
|
||||
data := test_obj["data"]
|
||||
input := load_json(data)
|
||||
partials := test_obj["partials"]
|
||||
partials_input := load_json(partials).(JSON_Map)
|
||||
|
||||
assert_mustache(t, template, input, exp_output, partials_input)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Someday.
|
||||
// @(test)
|
||||
// test_delimiters_spec :: proc(t: ^testing.T) {
|
||||
// spec := load_spec(DELIMITERS_SPEC)
|
||||
// defer json.destroy_value(spec)
|
||||
|
||||
// root := spec.(json.Object)
|
||||
// tests := root["tests"].(json.Array)
|
||||
|
||||
// for test, i in tests {
|
||||
// if i > 0 do break
|
||||
|
||||
// test_obj := test.(json.Object)
|
||||
// test_name := test_obj["name"].(string)
|
||||
// test_desc := test_obj["desc"].(string)
|
||||
// template := test_obj["template"].(string)
|
||||
// exp_output := test_obj["expected"].(string)
|
||||
// data := test_obj["data"]
|
||||
// input := load_json(data)
|
||||
|
||||
// // Not all the test cases have partials.
|
||||
// partials := test_obj["partials"]
|
||||
// partials_input, ok := load_json(partials).(Map)
|
||||
// if !ok {
|
||||
// partials_input = Map{}
|
||||
// }
|
||||
|
||||
// assert_mustache(t, template, input, exp_output, partials_input)
|
||||
// }
|
||||
// }
|
||||
|
||||
@(test)
|
||||
test_map_get :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
// Get the value in a map with one key.
|
||||
output: any
|
||||
mss := make(map[string]string)
|
||||
mcs := make(map[cstring]string)
|
||||
msi := make(map[string]int)
|
||||
mis := make(map[int]string)
|
||||
|
||||
tm := make(Test_Map)
|
||||
mtm := make(map[string]Test_Map)
|
||||
|
||||
u: Test_Data
|
||||
u = make(Test_Map)
|
||||
|
||||
// Simple map
|
||||
delete(mss)
|
||||
mss["name"] = "George"
|
||||
output, _ = map_get(mss, "name")
|
||||
testing.expect_value(t, output.(string), "George")
|
||||
|
||||
// Get the value in a map with one key when key is cstring.
|
||||
delete(mcs)
|
||||
mcs["name"] = "George"
|
||||
output, _ = map_get(mcs, "name")
|
||||
testing.expect_value(t, output.(string), "George")
|
||||
|
||||
// Get the first value in a map with multiple keys.
|
||||
delete(mss)
|
||||
mss["name"] = "George"
|
||||
mss["hometown"] = "Helena"
|
||||
output, _ = map_get(mss, "name")
|
||||
testing.expect_value(t, output.(string), "George")
|
||||
|
||||
// Get the second value in a map with multiple keys.
|
||||
delete(mss)
|
||||
mss["name"] = "George"
|
||||
mss["hometown"] = "Helena"
|
||||
output, _ = map_get(mss, "hometown")
|
||||
testing.expect_value(t, output.(string), "Helena")
|
||||
|
||||
// Get an int
|
||||
delete(msi)
|
||||
msi["phone_number"] = 5555555555
|
||||
output, _ = map_get(msi, "phone_number")
|
||||
testing.expect_value(t, output.(int), 5555555555)
|
||||
|
||||
// Nested, named map.
|
||||
delete(tm)
|
||||
delete(mtm)
|
||||
tm["name"] = "Lee"
|
||||
mtm["person"] = tm
|
||||
output, _ = map_get(mtm, "person")
|
||||
testing.expect_value(t, output.(Test_Map)["name"], tm["name"])
|
||||
|
||||
// Extract from a map type inside a union.
|
||||
delete(u.(Test_Map))
|
||||
(&u.(Test_Map))["name"] = "St. Charles"
|
||||
output, _ = map_get(u, "name")
|
||||
testing.expect_value(t, output.(string), u.(Test_Map)["name"])
|
||||
|
||||
// Return nil when the map is NOT keyed by string, cstring
|
||||
delete(mis)
|
||||
mis[1] = "Customer 1"
|
||||
output, _ = map_get(mis, "1")
|
||||
testing.expect(t, reflect.is_nil(output))
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_struct_get :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
output: any
|
||||
|
||||
data := Test_Struct{"Vincent", "foo@example.com"}
|
||||
output = struct_get(data, "name")
|
||||
testing.expect_value(t, output.(string), "Vincent")
|
||||
|
||||
output = struct_get(data, "email")
|
||||
testing.expect_value(t, output.(string), "foo@example.com")
|
||||
|
||||
output = struct_get("foo", "email")
|
||||
testing.expect(
|
||||
t,
|
||||
reflect.is_nil(output),
|
||||
"String argument that is NOT a Struct returns nil",
|
||||
)
|
||||
|
||||
test_map := make(Test_Map)
|
||||
test_map["name"] = "Lee"
|
||||
output = struct_get(test_map, "name")
|
||||
testing.expect(
|
||||
t,
|
||||
reflect.is_nil(output),
|
||||
"Map argument that is NOT a Struct returns nil",
|
||||
)
|
||||
|
||||
// Extract from a map type inside a union.
|
||||
u_struct: Test_Data
|
||||
u_struct = Test_Struct{"St. Charles", "foo@example.com"}
|
||||
output = struct_get(u_struct, "name")
|
||||
testing.expect_value(t, output.(string), u_struct.(Test_Struct).name)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_is_map :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
m := map[string]string{"Vincent" = "Edgar"}
|
||||
assert(t, is_map(m), "Regular map should return true")
|
||||
delete(m)
|
||||
|
||||
m = Test_Map{"name" = "Lee"}
|
||||
assert(t, is_map(m), "Named map should return true")
|
||||
delete(m)
|
||||
|
||||
data: Test_Data
|
||||
data = Test_Map{ "name" = "Vincent" }
|
||||
assert(
|
||||
t,
|
||||
is_map(data),
|
||||
"Union with Map variant should be considered a Map",
|
||||
)
|
||||
delete(data.(Test_Map))
|
||||
|
||||
data = Test_List{ "foo", "bar", "baz" }
|
||||
assert_not(
|
||||
t,
|
||||
is_map(data),
|
||||
"Union with list variant should not be considered a Map",
|
||||
)
|
||||
delete(data.(Test_List))
|
||||
|
||||
assert_not(
|
||||
t,
|
||||
is_map(Test_Struct{"Vincent", "foo@example.com"}),
|
||||
"Struct should not be a map",
|
||||
)
|
||||
|
||||
u: Test_Data
|
||||
u = Test_Struct{"Vincent", "foo@example.com"}
|
||||
assert_not(
|
||||
t,
|
||||
is_map(u),
|
||||
"Struct variant of a Union should not be considered a Map",
|
||||
)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_is_list :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
arr := [1]string{"element1"}
|
||||
assert(t, is_list(arr), "Array should be considered a list")
|
||||
assert(t, is_list(arr[:]), "Slice should be considered a list")
|
||||
|
||||
dyn_arr := make([dynamic]string, 1, 1)
|
||||
append(&dyn_arr, "element1")
|
||||
assert(t, is_list(dyn_arr), "Dynamic array should be considered a list")
|
||||
|
||||
u_arr: Test_Data
|
||||
u_arr = make(Test_List, 1, 1)
|
||||
append(&u_arr.(Test_List), "element1")
|
||||
assert(t, is_list(u_arr), "Dynamic array in a union should be considered a list")
|
||||
|
||||
assert_not(t, is_list("foo"), "string should not be considered a list")
|
||||
assert_not(t, is_list(1), "int should not be considered a list")
|
||||
|
||||
u_map: Test_Data
|
||||
u_map = make(Test_Map, 1)
|
||||
assert_not(
|
||||
t,
|
||||
is_list(u_map),
|
||||
"Map in union that has list type should not be considered a list",
|
||||
)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_is_struct :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
assert(
|
||||
t,
|
||||
is_struct(Test_Struct{"Vincent", "foo@example.com"}),
|
||||
"Struct should be considered a Struct",
|
||||
)
|
||||
|
||||
u: Test_Data
|
||||
u = Test_Struct{"Vincent", "foo@example.com"}
|
||||
assert(
|
||||
t,
|
||||
is_struct(u),
|
||||
"Struct variant of a union should be considered a Struct",
|
||||
)
|
||||
|
||||
data: Test_Data
|
||||
data = make(Test_Map)
|
||||
assert_not(
|
||||
t,
|
||||
is_struct(data),
|
||||
"Union with map variant should not be considered a Struct",
|
||||
)
|
||||
|
||||
data = make(Test_List)
|
||||
append(&data.(Test_List), "foo", "bar", "baz")
|
||||
assert_not(
|
||||
t,
|
||||
is_struct(data),
|
||||
"Union with list variant should not be considered a Struct",
|
||||
)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_is_union :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
u: Test_Data
|
||||
u = make(Test_Map)
|
||||
assert(
|
||||
t,
|
||||
is_union(u),
|
||||
"Union is union",
|
||||
)
|
||||
|
||||
|
||||
tm := make(Test_Map)
|
||||
assert_not(
|
||||
t,
|
||||
is_union(tm),
|
||||
"Union member with a type is not a union",
|
||||
)
|
||||
|
||||
tl := make(Test_List)
|
||||
assert_not(
|
||||
t,
|
||||
is_union(tl),
|
||||
"Union member with a type is not a union",
|
||||
)
|
||||
|
||||
m := make(map[string]string)
|
||||
assert_not(
|
||||
t,
|
||||
is_union(m),
|
||||
"Map should not be a union",
|
||||
)
|
||||
|
||||
ts := Test_Struct{"Vincent", "foo@example.com"}
|
||||
assert_not(
|
||||
t,
|
||||
is_union(ts),
|
||||
"Struct should not be a union",
|
||||
)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_data_len :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
data: any
|
||||
|
||||
data = make([dynamic]string)
|
||||
testing.expect_value(t, data_len(data), 0)
|
||||
|
||||
data = make([dynamic]string)
|
||||
append(&data.([dynamic]string), "Vincent")
|
||||
testing.expect_value(t, data_len(data), 1)
|
||||
|
||||
data = "FooBar"
|
||||
testing.expect_value(t, data_len(data), 6)
|
||||
|
||||
data = Test_Struct{"Vincent", "foo@example.com"}
|
||||
testing.expect_value(t, data_len(data), 2)
|
||||
|
||||
u: Test_Data
|
||||
u = Test_Struct{"Vincent", "foo@example.com"}
|
||||
testing.expect_value(t, data_len(u), 2)
|
||||
|
||||
u = make(Test_Map)
|
||||
(&u.(Test_Map))["name"] = "St. Charles"
|
||||
testing.expect_value(t, data_len(u), 1)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_has_key :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
data: any
|
||||
|
||||
data = Test_Struct{"St. Charles", "foo@example.com"}
|
||||
assert(t, has_key(data, "name"), "Should return true if stuct has field")
|
||||
|
||||
data = make(map[string]int)
|
||||
(&data.(map[string]int))["A1"] = 1
|
||||
assert(t, has_key(data, "A1"), "Should return true if map has key")
|
||||
assert_not(t, has_key(data, "B2"), "Should return false if map does not have key")
|
||||
|
||||
u: Test_Data
|
||||
u = make(Test_Map)
|
||||
(&u.(Test_Map))["name"] = "St. Charles"
|
||||
assert(t, has_key(u, "name"), "Should return true if union-map has key")
|
||||
assert_not(t, has_key(u, "email"), "Should return false if union-map does not have key")
|
||||
|
||||
u = Test_Struct{"St. Charles", "foo@example.com"}
|
||||
assert(t, has_key(u, "name"), "Should return true if union-struct has key")
|
||||
assert_not(t, has_key(u, "XXX"), "Should return false if union-struct does not have key")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_list_at :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
arr := [2]string{"foo", "bar"}
|
||||
testing.expect_value(t, list_at(arr, 0).(string), "foo")
|
||||
testing.expect_value(t, list_at(arr, 1).(string), "bar")
|
||||
|
||||
testing.expect_value(t, list_at(arr[:], 0).(string), "foo")
|
||||
testing.expect_value(t, list_at(arr[:], 1).(string), "bar")
|
||||
|
||||
dyn := slice.clone_to_dynamic(arr[:])
|
||||
testing.expect_value(t, list_at(dyn, 0).(string), "foo")
|
||||
testing.expect_value(t, list_at(dyn, 1).(string), "bar")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_dig :: proc(t: ^testing.T) {
|
||||
context.allocator = context.temp_allocator
|
||||
|
||||
output: any
|
||||
keys := make([dynamic]string)
|
||||
|
||||
// Pull out a struct value
|
||||
d1: any
|
||||
d1 = Test_Struct{"Vincent", "foo@example.com"}
|
||||
append(&keys, "name")
|
||||
output = dig(d1, keys[:])
|
||||
testing.expect_value(t, output.(string), "Vincent")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a map value
|
||||
d2: any
|
||||
d2 = make(map[string]string)
|
||||
(&d2.(map[string]string))["name"] = "Edgar"
|
||||
keys = {"name"}
|
||||
output = dig(d2, keys[:])
|
||||
testing.expect_value(t, output.(string), "Edgar")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a nested map value
|
||||
d3: any
|
||||
d3 = make(map[string]map[string]string)
|
||||
c1 := make(map[string]string)
|
||||
c1["name"] = "Kurt"
|
||||
c1["email"] = "test@example.com"
|
||||
(&d3.(map[string]map[string]string))["customer1"] = c1
|
||||
keys = {"customer1", "email"}
|
||||
output = dig(d3, keys[:])
|
||||
testing.expect_value(t, output.(string), "test@example.com")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a nested map
|
||||
d4 := make(map[string]map[string]string)
|
||||
c2 := make(map[string]string)
|
||||
c2["name"] = "Kurt"
|
||||
c2["email"] = "test@example.com"
|
||||
d4["customer1"] = c2
|
||||
keys = {"customer1"}
|
||||
output = dig(d4, keys[:])
|
||||
testing.expect_value(t, output.(map[string]string)["name"], "Kurt")
|
||||
testing.expect_value(t, output.(map[string]string)["email"], "test@example.com")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a list
|
||||
d5: any
|
||||
d5 = Test_List{"El1", "El2"}
|
||||
keys = {"key1"}
|
||||
output = dig(d5, keys[:])
|
||||
testing.expect_value(t, output.(Test_List)[0], d5.(Test_List)[0])
|
||||
testing.expect_value(t, output.(Test_List)[1], d5.(Test_List)[1])
|
||||
delete(d5.(Test_List))
|
||||
delete(keys)
|
||||
|
||||
// Pull out a struct inside a map
|
||||
d6: any
|
||||
d6 = make(map[string]Test_Struct)
|
||||
c3 := Test_Struct{"Vincent", "foo@example.com"}
|
||||
(&d6.(map[string]Test_Struct))["customer1"] = c3
|
||||
keys = {"customer1", "email"}
|
||||
output = dig(d6, keys[:])
|
||||
testing.expect_value(t, output.(string), "foo@example.com")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a string with dot notation
|
||||
d7 := "Hello, world!"
|
||||
keys = {"."}
|
||||
output = dig(d7, keys[:])
|
||||
testing.expect_value(t, output.(string), "Hello, world!")
|
||||
delete(keys)
|
||||
|
||||
// Return nil when string and not dot notation.
|
||||
d8 := "Hello, world!"
|
||||
keys = {"XXX"}
|
||||
output = dig(d8, keys[:])
|
||||
assert(t, reflect.is_nil(output), "Nil string when a key that is not '.' is provided.")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a nil struct value
|
||||
d9 := Test_Struct{"Vincent", "foo@example.com"}
|
||||
keys = {"XXX"}
|
||||
output = dig(d9, keys[:])
|
||||
assert(t, reflect.is_nil(output), "Struct without a matching field should be nil")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a nil struct value with multiple keys
|
||||
d10 := Test_Struct{"Vincent", "foo@example.com"}
|
||||
keys = {"XXX", "YYY"}
|
||||
output = dig(d10, keys[:])
|
||||
assert(t, reflect.is_nil(output), "Struct without a matching field should be nil")
|
||||
delete(keys)
|
||||
|
||||
// Pull out a nil struct value
|
||||
d11 := make(map[string]string)
|
||||
d11["name"] = "Vincent"
|
||||
keys = {"XXX"}
|
||||
output = dig(d11, keys[:])
|
||||
assert(t, reflect.is_nil(output), "Map without a matching field should be nil")
|
||||
delete(keys)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user