Repository navigation
Expand file tree
/
Copy pathnullable.go
More file actions
74 lines (64 loc) · 2.8 KB
/
Copy pathnullable.go
File metadata and controls
74 lines (64 loc) · 2.8 KB
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
// Copyright 2024 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package protocol
import (
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
)
// Nullable wraps a value that is BOTH optional and JSON-nullable, distinguishing
// the three wire states the LSP specification assigns distinct meaning to:
//
// - absent — the zero Nullable; omitted on marshal via the ",omitzero" tag;
// - null — an explicit JSON null;
// - value — a present value.
//
// It is generated only for properties that are simultaneously optional and have
// a null arm (e.g. WorkspaceFoldersInitializeParams.workspaceFolders, where
// absent means "no workspace-folder support" and null means "supported, none
// open"). A plain pointer cannot represent all three states.
type Nullable[T any] struct {
value T
set bool
null bool
}
// NewNullable returns a Nullable holding the present value v (set=true, null=false).
func NewNullable[T any](v T) Nullable[T] { return Nullable[T]{set: true, value: v} }
// NullNullable returns a Nullable that represents an explicit JSON null
// (set=true, null=true). This is the second non-zero tri-state; the third
// (absent) needs no constructor because it is the zero Nullable[T]{}.
func NullNullable[T any]() Nullable[T] { return Nullable[T]{set: true, null: true} }
// IsZero reports whether the value is absent. It drives the ",omitzero" tag so
// an unset Nullable is omitted entirely.
func (n Nullable[T]) IsZero() bool { return !n.set }
// IsNull reports whether the value is present as an explicit JSON null.
func (n Nullable[T]) IsNull() bool { return n.set && n.null }
// Get returns the wrapped value and whether a non-null value is present.
func (n Nullable[T]) Get() (T, bool) { return n.value, n.set && !n.null }
// MarshalJSONTo implements json.MarshalerTo, streaming the wrapped value (or
// null) through enc so encoder options propagate without materializing
// intermediate bytes per field.
func (n Nullable[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
if n.null {
return enc.WriteToken(jsontext.Null)
}
return json.MarshalEncode(enc, &n.value)
}
// UnmarshalJSONFrom implements json.UnmarshalerFrom. It decodes the value in
// place on the caller's decoder — re-applying the union unmarshalers so a
// nested union value dispatches even when the decoder was not built by
// [Unmarshal] — instead of materializing the value bytes and re-parsing them
// with a fresh decoder per field.
func (n *Nullable[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
n.set = true
if dec.PeekKind() == 'n' {
if _, err := dec.ReadToken(); err != nil {
return err
}
n.null = true
var zero T
n.value = zero
return nil
}
n.null = false
return json.UnmarshalDecode(dec, &n.value, json.WithUnmarshalers(unionUnmarshalers))
}