返回 DeepSeek-Reasonix
types.go
根目录 / desktop / internal / hostrpc / types.go
1 package hostrpc
2
3 import (
4 "encoding"
5 "encoding/json"
6 "fmt"
7 "reflect"
8 "strings"
9 "time"
10 )
11
12 // Kind is the JSON shape a TypeRef describes.
13 type Kind string
14
15 const (
16 KindString Kind = "string"
17 KindNumber Kind = "number"
18 KindInteger Kind = "integer"
19 KindBoolean Kind = "boolean"
20 KindAny Kind = "any"
21 KindArray Kind = "array"
22 KindMap Kind = "map"
23 KindObject Kind = "object"
24 KindNullable Kind = "nullable"
25 )
26
27 // TypeRef describes one JSON value shape. Elem is the element of an array,
28 // map or nullable; Key is a map's key shape; Ref names an entry of
29 // Contract.Types for objects.
30 type TypeRef struct {
31 Kind Kind `json:"kind"`
32 Elem *TypeRef `json:"elem,omitempty"`
33 Key *TypeRef `json:"key,omitempty"`
34 Ref string `json:"ref,omitempty"`
35 }
36
37 // Field is one member of an ObjectType. Optional marks members the encoder
38 // may omit (omitempty, omitzero) or emit as null (pointers).
39 type Field struct {
40 Name string `json:"name"`
41 Type TypeRef `json:"type"`
42 Optional bool `json:"optional,omitempty"`
43 }
44
45 // ObjectType is a Go struct as encoding/json writes it.
46 type ObjectType struct {
47 Fields []Field `json:"fields"`
48 }
49
50 var (
51 timeType = reflect.TypeFor[time.Time]()
52 rawMessageType = reflect.TypeFor[json.RawMessage]()
53 textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()
54 jsonMarshalerType = reflect.TypeFor[json.Marshaler]()
55 errorType = reflect.TypeFor[error]()
56 )
57
58 // typeCollector builds TypeRefs and gathers every named struct it crosses,
59 // keyed by pkg.Name, so a contract lists each DTO exactly once.
60 type typeCollector struct {
61 types map[string]ObjectType
62 named map[reflect.Type]string
63 }
64
65 func newTypeCollector() *typeCollector {
66 return &typeCollector{types: map[string]ObjectType{}, named: map[reflect.Type]string{}}
67 }
68
69 // ref describes t. hint names an anonymous struct reached through t.
70 func (c *typeCollector) ref(t reflect.Type, hint string) (TypeRef, error) {
71 switch {
72 case t == timeType:
73 return TypeRef{Kind: KindString}, nil
74 case t == rawMessageType:
75 return TypeRef{Kind: KindAny}, nil
76 case !implements(t, jsonMarshalerType) && implements(t, textMarshalerType):
77 return TypeRef{Kind: KindString}, nil
78 }
79 switch t.Kind() {
80 case reflect.Interface:
81 return TypeRef{Kind: KindAny}, nil
82 case reflect.Pointer:
83 return c.wrap(KindNullable, t.Elem(), hint)
84 case reflect.String:
85 return TypeRef{Kind: KindString}, nil
86 case reflect.Bool:
87 return TypeRef{Kind: KindBoolean}, nil
88 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
89 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
90 return TypeRef{Kind: KindInteger}, nil
91 case reflect.Float32, reflect.Float64:
92 return TypeRef{Kind: KindNumber}, nil
93 case reflect.Slice:
94 if t.Elem().Kind() == reflect.Uint8 && !implements(t.Elem(), jsonMarshalerType) {
95 return TypeRef{Kind: KindString}, nil
96 }
97 return c.wrap(KindArray, t.Elem(), hint)
98 case reflect.Array:
99 return c.wrap(KindArray, t.Elem(), hint)
100 case reflect.Map:
101 return c.mapRef(t, hint)
102 case reflect.Struct:
103 return c.structRef(t, hint)
104 }
105 return TypeRef{}, fmt.Errorf("%s is not JSON-serialisable", t)
106 }
107
108 func (c *typeCollector) wrap(kind Kind, elem reflect.Type, hint string) (TypeRef, error) {
109 ref, err := c.ref(elem, hint)
110 if err != nil {
111 return TypeRef{}, err
112 }
113 return TypeRef{Kind: kind, Elem: &ref}, nil
114 }
115
116 func (c *typeCollector) mapRef(t reflect.Type, hint string) (TypeRef, error) {
117 var key TypeRef
118 switch kt := t.Key(); {
119 case kt.Kind() == reflect.String || implements(kt, textMarshalerType):
120 key = TypeRef{Kind: KindString}
121 case isIntegerKind(kt.Kind()):
122 key = TypeRef{Kind: KindInteger}
123 default:
124 return TypeRef{}, fmt.Errorf("%s: map key %s is not JSON-serialisable", t, kt)
125 }
126 elem, err := c.ref(t.Elem(), hint)
127 if err != nil {
128 return TypeRef{}, err
129 }
130 return TypeRef{Kind: KindMap, Key: &key, Elem: &elem}, nil
131 }
132
133 func (c *typeCollector) structRef(t reflect.Type, hint string) (TypeRef, error) {
134 name, seen := c.named[t]
135 if seen {
136 return TypeRef{Kind: KindObject, Ref: name}, nil
137 }
138 // Type.String uses the package name, so "main.TabMeta" is the same key in
139 // the shipped binary and in the test binary that compiles package main
140 // under its import path; PkgPath would split the digest between them.
141 name = hint
142 if t.Name() != "" {
143 name = t.String()
144 }
145 if _, taken := c.types[name]; taken {
146 return TypeRef{}, fmt.Errorf("%s: type name %q is already used by another type", t, name)
147 }
148 c.named[t] = name
149 c.types[name] = ObjectType{}
150 fields, err := c.fields(t, name)
151 if err != nil {
152 return TypeRef{}, fmt.Errorf("%s: %w", name, err)
153 }
154 c.types[name] = ObjectType{Fields: fields}
155 return TypeRef{Kind: KindObject, Ref: name}, nil
156 }
157
158 type fieldCandidate struct {
159 field Field
160 depth int
161 tagged bool
162 }
163
164 func (c *typeCollector) fields(t reflect.Type, owner string) ([]Field, error) {
165 var found []fieldCandidate
166 if err := c.walkFields(t, owner, 0, &found); err != nil {
167 return nil, err
168 }
169 return dominantFields(found), nil
170 }
171
172 // walkFields mirrors encoding/json: embedded structs without a tag name are
173 // flattened, tagged or non-struct embeds become members named after the type.
174 func (c *typeCollector) walkFields(t reflect.Type, owner string, depth int, out *[]fieldCandidate) error {
175 for i := range t.NumField() {
176 f := t.Field(i)
177 tag := f.Tag.Get("json")
178 if tag == "-" {
179 continue
180 }
181 name, opts, _ := strings.Cut(tag, ",")
182 if f.Anonymous && name == "" {
183 et := f.Type
184 if et.Kind() == reflect.Pointer {
185 et = et.Elem()
186 }
187 if et.Kind() == reflect.Struct {
188 if err := c.walkFields(et, owner, depth+1, out); err != nil {
189 return err
190 }
191 continue
192 }
193 }
194 if !f.IsExported() {
195 continue
196 }
197 if name == "" {
198 name = f.Name
199 }
200 ref, err := c.ref(f.Type, owner+"."+f.Name)
201 if err != nil {
202 return fmt.Errorf("field %s: %w", f.Name, err)
203 }
204 if hasOption(opts, "string") && quotable(f.Type) {
205 ref = TypeRef{Kind: KindString}
206 }
207 optional := hasOption(opts, "omitempty") || hasOption(opts, "omitzero") || f.Type.Kind() == reflect.Pointer
208 *out = append(*out, fieldCandidate{
209 field: Field{Name: name, Type: ref, Optional: optional},
210 depth: depth,
211 tagged: tag != "",
212 })
213 }
214 return nil
215 }
216
217 // dominantFields applies encoding/json's shadowing: the shallowest member
218 // wins, a tagged one breaks a tie, an unresolved tie drops the name.
219 func dominantFields(found []fieldCandidate) []Field {
220 byName := map[string][]fieldCandidate{}
221 var order []string
222 for _, cand := range found {
223 if _, ok := byName[cand.field.Name]; !ok {
224 order = append(order, cand.field.Name)
225 }
226 byName[cand.field.Name] = append(byName[cand.field.Name], cand)
227 }
228 out := make([]Field, 0, len(order))
229 for _, name := range order {
230 if f, ok := dominant(byName[name]); ok {
231 out = append(out, f)
232 }
233 }
234 return out
235 }
236
237 func dominant(cands []fieldCandidate) (Field, bool) {
238 minDepth := cands[0].depth
239 for _, cand := range cands[1:] {
240 minDepth = min(minDepth, cand.depth)
241 }
242 var shallow, tagged []fieldCandidate
243 for _, cand := range cands {
244 if cand.depth != minDepth {
245 continue
246 }
247 shallow = append(shallow, cand)
248 if cand.tagged {
249 tagged = append(tagged, cand)
250 }
251 }
252 switch {
253 case len(shallow) == 1:
254 return shallow[0].field, true
255 case len(tagged) == 1:
256 return tagged[0].field, true
257 }
258 return Field{}, false
259 }
260
261 func hasOption(opts, want string) bool {
262 for opts != "" {
263 var opt string
264 opt, opts, _ = strings.Cut(opts, ",")
265 if opt == want {
266 return true
267 }
268 }
269 return false
270 }
271
272 func quotable(t reflect.Type) bool {
273 if t.Kind() == reflect.Pointer {
274 t = t.Elem()
275 }
276 switch k := t.Kind(); {
277 case k == reflect.String, k == reflect.Bool, isIntegerKind(k), k == reflect.Float32, k == reflect.Float64:
278 return true
279 }
280 return false
281 }
282
283 func isIntegerKind(k reflect.Kind) bool {
284 return k >= reflect.Int && k <= reflect.Uintptr
285 }
286
287 func implements(t, iface reflect.Type) bool {
288 return t.Implements(iface) || reflect.PointerTo(t).Implements(iface)
289 }
290
290 lines GO