mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 19:33:32 -04:00
961 lines
24 KiB
Odin
961 lines
24 KiB
Odin
#+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)
|
|
}
|