-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathref.go
106 lines (87 loc) · 3.07 KB
/
ref.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
package openapi
import (
"bytes"
"fmt"
"strings"
"github.com/MarkRosemaker/errpath"
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
)
// Reference is a simple object to allow referencing other components in the OpenAPI document, internally and externally.
//
// The `$ref` string value contains a URI RFC3986, which identifies the location of the value being referenced.
//
// See the rules for resolving Relative References.
//
// ([Specification])
//
// [Specification]: https://spec.openapis.org/oas/v3.1.0#reference-object
type Reference struct {
// REQUIRED. The reference identifier. This MUST be in the form of a URI.
Identifier string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
// A short summary which by default SHOULD override that of the referenced component. If the referenced object-type does not allow a `summary` field, then this field has no effect.
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
// A description which by default SHOULD override that of the referenced component. CommonMark syntax MAY be used for rich text representation. If the referenced object-type does not allow a `description` field, then this field has no effect.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
}
func (r *Reference) Validate() error {
if r.Identifier == "" {
return &errpath.ErrField{Field: "$ref", Err: &errpath.ErrRequired{}}
}
r.Description = strings.TrimSpace(r.Description)
return nil
}
type referencable[T any] interface {
Validate() error
*T
}
// refOrValue is a reference to a component or the component itself.
type refOrValue[T any, O referencable[T]] struct {
// The referenced object.
Value O `json:",inline" yaml:",inline"`
// The reference.
Ref *Reference `json:",inline" yaml:",inline"`
// an index to the original location of this object
idx int
}
func (r *refOrValue[T, O]) Validate() error {
if r.Ref != nil {
if r.Value == nil {
return fmt.Errorf("%s (%T) was not resolved", r.Ref.Identifier, r.Value)
}
return r.Ref.Validate()
}
return r.Value.Validate()
}
func (r *refOrValue[T, O]) UnmarshalJSONFrom(dec *jsontext.Decoder, opts json.Options) error {
// we don't know if this is a reference or not, so we read the value first
val, err := dec.ReadValue()
if err != nil {
return err
}
// try to unmarshal as a reference
ref := &Reference{}
if err := json.UnmarshalDecode(
jsontext.NewDecoder(bytes.NewBuffer(val), opts), ref, opts,
); err == nil && ref.Identifier != "" {
// we successfully unmarshalled as a reference
r.Ref = ref // set the reference
return nil
}
// it is not a reference, unmarshal as object
var v O
if err := json.UnmarshalDecode(
jsontext.NewDecoder(bytes.NewBuffer(val), opts), &v, opts,
); err != nil {
var t T
return fmt.Errorf("value of %T: %w", t, err)
}
r.Value = v // set the value
return nil
}
func (r *refOrValue[_, _]) MarshalJSONTo(enc *jsontext.Encoder, opts json.Options) error {
if r.Ref == nil {
return json.MarshalEncode(enc, r.Value, opts)
}
return json.MarshalEncode(enc, r.Ref, opts)
}