respjson.go

 1package respjson
 2
 3// A Field provides metadata to indicate the presence of a value.
 4//
 5// Use [Field.Valid] to check if an optional value was null or omitted.
 6//
 7// A Field will always occur in the following structure, where it
 8// mirrors the original field in it's parent struct:
 9//
10//	type ExampleObject struct {
11//		Foo bool	`json:"foo"`
12//		Bar int		`json:"bar"`
13//		// ...
14//
15//		// JSON provides metadata about the object.
16//		JSON struct {
17//			Foo Field
18//			Bar Field
19//			// ...
20//		} `json:"-"`
21//	}
22//
23// To differentiate a "nullish" value from the zero value,
24// use the [Field.Valid] method.
25//
26//	if !example.JSON.Foo.Valid() {
27//		println("Foo is null or omitted")
28//	}
29//
30//	if example.Foo {
31//		println("Foo is true")
32//	} else {
33//		println("Foo is false")
34//	}
35//
36// To differentiate if a field was omitted or the JSON value "null",
37// use the [Field.Raw] method.
38//
39//	if example.JSON.Foo.Raw() == "null" {
40//		println("Foo is null")
41//	}
42//
43//	if example.JSON.Foo.Raw() == "" {
44//		println("Foo was omitted")
45//	}
46//
47// Otherwise, if the field was invalid and couldn't be marshalled successfully,
48// [Field.Valid] will be false and [Field.Raw] will not be empty.
49type Field struct {
50	status
51	raw string
52}
53
54const (
55	omitted status = iota
56	null
57	invalid
58	valid
59)
60
61type status int8
62
63// Valid returns true if the parent field was set.
64// Valid returns false if the value doesn't exist, is JSON null, or
65// is an unexpected type.
66func (j Field) Valid() bool { return j.status > invalid }
67
68const Null string = "null"
69const Omitted string = ""
70
71// Returns the raw JSON value of the field.
72func (j Field) Raw() string {
73	if j.status == omitted {
74		return ""
75	}
76	return j.raw
77}
78
79func NewField(raw string) Field {
80	if raw == "null" {
81		return Field{status: null, raw: Null}
82	}
83	return Field{status: valid, raw: raw}
84}
85
86func NewInvalidField(raw string) Field {
87	return Field{status: invalid, raw: raw}
88}