forked from sampork/go-sniffer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fjson.go
148 lines (135 loc) · 4.49 KB
/
fjson.go
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2021 FerretDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package fjson provides converters to FJSON (JSON with some extensions) for built-in and `types` types.
//
// See contributing guidelines and documentation for package `types` for details.
//
// # Mapping
//
// Composite types
//
// Alias types package fjson package JSON representation
//
// object *types.Document *fjson.documentType {"$k": ["<key 1>", "<key 2>", ...], "<key 1>": <value 1>, "<key 2>": <value 2>, ...}
// array *types.Array *fjson.arrayType JSON array
//
// Scalar types
//
// Alias types package fjson package JSON representation
//
// double float64 *fjson.doubleType {"$f": JSON number} or {"$f": "Infinity|-Infinity|NaN"}
// string string *fjson.stringType JSON string
// binData types.Binary *fjson.binaryType {"$b": "<base 64 string>", "s": <subtype number>}
// objectId types.ObjectID *fjson.objectIDType {"$o": "<ObjectID as 24 character hex string"}
// bool bool *fjson.boolType JSON true / false values
// date time.Time *fjson.dateTimeType {"$d": milliseconds since epoch as JSON number}
// null types.NullType *fjson.nullType JSON null
// regex types.Regex *fjson.regexType {"$r": "<string without terminating 0x0>", "o": "<string without terminating 0x0>"}
// int int32 *fjson.int32Type JSON number
// timestamp types.Timestamp *fjson.timestampType {"$t": "<number as string>"}
// long int64 *fjson.int64Type {"$l": "<number as string>"}
//
//nolint:lll // for readability
//nolint:dupword // false positive
package fjson
import (
"encoding/json"
"fmt"
"time"
"github.com/AlekSi/pointer"
"github.com/40t/go-sniffer/plugSrc/mongodb/build/FerretDB-internal/types"
"github.com/40t/go-sniffer/plugSrc/mongodb/build/FerretDB-internal/util/lazyerrors"
)
// fjsontype is a type that can be marshaled to FJSON.
//
//sumtype:decl
type fjsontype interface {
fjsontype() // seal for sumtype
json.Marshaler
}
// fromFJSON converts fjsontype value to matching built-in or types' package value.
func fromFJSON(v fjsontype) any {
switch v := v.(type) {
case *documentType:
return pointer.To(types.Document(*v))
case *arrayType:
return pointer.To(types.Array(*v))
case *doubleType:
return float64(*v)
case *stringType:
return string(*v)
case *binaryType:
return types.Binary(*v)
case *objectIDType:
return types.ObjectID(*v)
case *boolType:
return bool(*v)
case *dateTimeType:
return time.Time(*v)
case *nullType:
return types.Null
case *regexType:
return types.Regex(*v)
case *int32Type:
return int32(*v)
case *timestampType:
return types.Timestamp(*v)
case *int64Type:
return int64(*v)
}
panic(fmt.Sprintf("not reached: %T", v)) // for sumtype to work
}
// toFJSON converts built-in or types' package value to fjsontype value.
func toFJSON(v any) fjsontype {
switch v := v.(type) {
case *types.Document:
return pointer.To(documentType(*v))
case *types.Array:
return pointer.To(arrayType(*v))
case float64:
return pointer.To(doubleType(v))
case string:
return pointer.To(stringType(v))
case types.Binary:
return pointer.To(binaryType(v))
case types.ObjectID:
return pointer.To(objectIDType(v))
case bool:
return pointer.To(boolType(v))
case time.Time:
return pointer.To(dateTimeType(v))
case types.NullType:
return pointer.To(nullType(v))
case types.Regex:
return pointer.To(regexType(v))
case int32:
return pointer.To(int32Type(v))
case types.Timestamp:
return pointer.To(timestampType(v))
case int64:
return pointer.To(int64Type(v))
}
panic(fmt.Sprintf("not reached: %T", v)) // for sumtype to work
}
// Marshal encodes given built-in or types' package value into fjson.
func Marshal(v any) ([]byte, error) {
if v == nil {
panic("v is nil")
}
b, err := toFJSON(v).MarshalJSON()
if err != nil {
return nil, lazyerrors.Error(err)
}
return b, nil
}