master
go 590 lines 13.6 KB
Raw
1 package config
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "io"
8 "strings"
9 "time"
10
11 humanize "github.com/dustin/go-humanize"
12 )
13
14 // Strings is a helper type that (un)marshals a single string to/from a single
15 // JSON string and a slice of strings to/from a JSON array of strings.
16 type Strings []string
17
18 // UnmarshalJSON conforms to the json.Unmarshaler interface.
19 func (o *Strings) UnmarshalJSON(data []byte) error {
20 if data[0] == '[' {
21 return json.Unmarshal(data, (*[]string)(o))
22 }
23 var value string
24 if err := json.Unmarshal(data, &value); err != nil {
25 return err
26 }
27 if len(value) == 0 {
28 *o = []string{}
29 } else {
30 *o = []string{value}
31 }
32 return nil
33 }
34
35 // MarshalJSON conforms to the json.Marshaler interface.
36 func (o Strings) MarshalJSON() ([]byte, error) {
37 switch len(o) {
38 case 0:
39 return json.Marshal(nil)
40 case 1:
41 return json.Marshal(o[0])
42 default:
43 return json.Marshal([]string(o))
44 }
45 }
46
47 var (
48 _ json.Unmarshaler = (*Strings)(nil)
49 _ json.Marshaler = (*Strings)(nil)
50 )
51
52 // Flag represents a ternary value: false (-1), default (0), or true (+1).
53 //
54 // When encoded in json, False is "false", Default is "null" (or empty), and True
55 // is "true".
56 type Flag int8
57
58 const (
59 False Flag = -1
60 Default Flag = 0
61 True Flag = 1
62 )
63
64 // WithDefault resolves the value of the flag given the provided default value.
65 //
66 // Panics if Flag is an invalid value.
67 func (f Flag) WithDefault(defaultValue bool) bool {
68 switch f {
69 case False:
70 return false
71 case Default:
72 return defaultValue
73 case True:
74 return true
75 default:
76 panic(fmt.Sprintf("invalid flag value %d", f))
77 }
78 }
79
80 func (f Flag) MarshalJSON() ([]byte, error) {
81 switch f {
82 case Default:
83 return json.Marshal(nil)
84 case True:
85 return json.Marshal(true)
86 case False:
87 return json.Marshal(false)
88 default:
89 return nil, fmt.Errorf("invalid flag value: %d", f)
90 }
91 }
92
93 func (f *Flag) UnmarshalJSON(input []byte) error {
94 switch string(input) {
95 case "null":
96 *f = Default
97 case "false":
98 *f = False
99 case "true":
100 *f = True
101 default:
102 return fmt.Errorf("failed to unmarshal %q into a flag: must be null/undefined, true, or false", string(input))
103 }
104 return nil
105 }
106
107 func (f Flag) String() string {
108 switch f {
109 case Default:
110 return "default"
111 case True:
112 return "true"
113 case False:
114 return "false"
115 default:
116 return fmt.Sprintf("<invalid flag value %d>", f)
117 }
118 }
119
120 // ResolveBoolFromConfig returns the resolved boolean value based on:
121 // - If userSet is true, returns userValue (user explicitly set the flag)
122 // - Otherwise, uses configFlag.WithDefault(defaultValue) (respects config or falls back to default)
123 func ResolveBoolFromConfig(userValue bool, userSet bool, configFlag Flag, defaultValue bool) bool {
124 if userSet {
125 return userValue
126 }
127 return configFlag.WithDefault(defaultValue)
128 }
129
130 var (
131 _ json.Unmarshaler = (*Flag)(nil)
132 _ json.Marshaler = (*Flag)(nil)
133 )
134
135 // Priority represents a value with a priority where 0 means "default" and -1
136 // means "disabled".
137 //
138 // When encoded in json, Default is encoded as "null" and Disabled is encoded as
139 // "false".
140 type Priority int64
141
142 const (
143 DefaultPriority Priority = 0
144 Disabled Priority = -1
145 )
146
147 // WithDefault resolves the priority with the given default.
148 //
149 // If defaultPriority is Default/0, this function will return 0.
150 //
151 // Panics if the priority has an invalid value (e.g., not DefaultPriority,
152 // Disabled, or > 0).
153 func (p Priority) WithDefault(defaultPriority Priority) (priority int64, enabled bool) {
154 switch p {
155 case Disabled:
156 return 0, false
157 case DefaultPriority:
158 switch defaultPriority {
159 case Disabled:
160 return 0, false
161 case DefaultPriority:
162 return 0, true
163 default:
164 if defaultPriority <= 0 {
165 panic(fmt.Sprintf("invalid priority %d < 0", int64(defaultPriority)))
166 }
167 return int64(defaultPriority), true
168 }
169 default:
170 if p <= 0 {
171 panic(fmt.Sprintf("invalid priority %d < 0", int64(p)))
172 }
173 return int64(p), true
174 }
175 }
176
177 func (p Priority) MarshalJSON() ([]byte, error) {
178 // > 0 == Priority
179 if p > 0 {
180 return json.Marshal(int64(p))
181 }
182 // <= 0 == special
183 switch p {
184 case DefaultPriority:
185 return json.Marshal(nil)
186 case Disabled:
187 return json.Marshal(false)
188 default:
189 return nil, fmt.Errorf("invalid priority value: %d", p)
190 }
191 }
192
193 func (p *Priority) UnmarshalJSON(input []byte) error {
194 switch string(input) {
195 case "null", "undefined":
196 *p = DefaultPriority
197 case "false":
198 *p = Disabled
199 case "true":
200 return fmt.Errorf("'true' is not a valid priority")
201 default:
202 var priority int64
203 err := json.Unmarshal(input, &priority)
204 if err != nil {
205 return err
206 }
207 if priority <= 0 {
208 return fmt.Errorf("priority must be positive: %d <= 0", priority)
209 }
210 *p = Priority(priority)
211 }
212 return nil
213 }
214
215 func (p Priority) String() string {
216 if p > 0 {
217 return fmt.Sprintf("%d", p)
218 }
219 switch p {
220 case DefaultPriority:
221 return "default"
222 case Disabled:
223 return "false"
224 default:
225 return fmt.Sprintf("<invalid priority %d>", p)
226 }
227 }
228
229 var (
230 _ json.Unmarshaler = (*Priority)(nil)
231 _ json.Marshaler = (*Priority)(nil)
232 )
233
234 // OptionalDuration wraps time.Duration to provide json serialization and deserialization.
235 //
236 // NOTE: the zero value encodes to JSON nill.
237 type OptionalDuration struct {
238 value *time.Duration
239 }
240
241 // NewOptionalDuration returns an OptionalDuration from a string.
242 func NewOptionalDuration(d time.Duration) *OptionalDuration {
243 return &OptionalDuration{value: &d}
244 }
245
246 func (d *OptionalDuration) UnmarshalJSON(input []byte) error {
247 switch string(input) {
248 case "null", "undefined", "\"null\"", "", "default", "\"\"", "\"default\"":
249 *d = OptionalDuration{}
250 return nil
251 default:
252 text := strings.Trim(string(input), "\"")
253 value, err := time.ParseDuration(text)
254 if err != nil {
255 return err
256 }
257 *d = OptionalDuration{value: &value}
258 return nil
259 }
260 }
261
262 func (d *OptionalDuration) IsDefault() bool {
263 return d == nil || d.value == nil
264 }
265
266 func (d *OptionalDuration) WithDefault(defaultValue time.Duration) time.Duration {
267 if d == nil || d.value == nil {
268 return defaultValue
269 }
270 return *d.value
271 }
272
273 func (d OptionalDuration) MarshalJSON() ([]byte, error) {
274 if d.value == nil {
275 return json.Marshal(nil)
276 }
277 return json.Marshal(d.value.String())
278 }
279
280 func (d OptionalDuration) String() string {
281 if d.value == nil {
282 return "default"
283 }
284 return d.value.String()
285 }
286
287 var (
288 _ json.Unmarshaler = (*OptionalDuration)(nil)
289 _ json.Marshaler = (*OptionalDuration)(nil)
290 )
291
292 type Duration struct {
293 time.Duration
294 }
295
296 func (d Duration) MarshalJSON() ([]byte, error) {
297 return json.Marshal(d.String())
298 }
299
300 func (d *Duration) UnmarshalJSON(b []byte) error {
301 var v any
302 if err := json.Unmarshal(b, &v); err != nil {
303 return err
304 }
305 switch value := v.(type) {
306 case float64:
307 d.Duration = time.Duration(value)
308 return nil
309 case string:
310 var err error
311 d.Duration, err = time.ParseDuration(value)
312 if err != nil {
313 return err
314 }
315 return nil
316 default:
317 return fmt.Errorf("unable to parse duration, expected a duration string or a float, but got %T", v)
318 }
319 }
320
321 var (
322 _ json.Unmarshaler = (*Duration)(nil)
323 _ json.Marshaler = (*Duration)(nil)
324 )
325
326 // OptionalInteger represents an integer that has a default value
327 //
328 // When encoded in json, Default is encoded as "null".
329 type OptionalInteger struct {
330 value *int64
331 }
332
333 // NewOptionalInteger returns an OptionalInteger from a int64.
334 func NewOptionalInteger(v int64) *OptionalInteger {
335 return &OptionalInteger{value: &v}
336 }
337
338 // WithDefault resolves the integer with the given default.
339 func (p *OptionalInteger) WithDefault(defaultValue int64) (value int64) {
340 if p == nil || p.value == nil {
341 return defaultValue
342 }
343 return *p.value
344 }
345
346 // IsDefault returns if this is a default optional integer.
347 func (p *OptionalInteger) IsDefault() bool {
348 return p == nil || p.value == nil
349 }
350
351 func (p OptionalInteger) MarshalJSON() ([]byte, error) {
352 if p.value != nil {
353 return json.Marshal(p.value)
354 }
355 return json.Marshal(nil)
356 }
357
358 func (p *OptionalInteger) UnmarshalJSON(input []byte) error {
359 switch string(input) {
360 case "null", "undefined":
361 *p = OptionalInteger{}
362 default:
363 var value int64
364 err := json.Unmarshal(input, &value)
365 if err != nil {
366 return err
367 }
368 *p = OptionalInteger{value: &value}
369 }
370 return nil
371 }
372
373 func (p OptionalInteger) String() string {
374 if p.value == nil {
375 return "default"
376 }
377 return fmt.Sprintf("%d", *p.value)
378 }
379
380 var (
381 _ json.Unmarshaler = (*OptionalInteger)(nil)
382 _ json.Marshaler = (*OptionalInteger)(nil)
383 )
384
385 // OptionalString represents a string that has a default value
386 //
387 // When encoded in json, Default is encoded as "null".
388 type OptionalString struct {
389 value *string
390 }
391
392 // NewOptionalString returns an OptionalString from a string.
393 func NewOptionalString(s string) *OptionalString {
394 return &OptionalString{value: &s}
395 }
396
397 // WithDefault resolves the integer with the given default.
398 func (p *OptionalString) WithDefault(defaultValue string) (value string) {
399 if p == nil || p.value == nil {
400 return defaultValue
401 }
402 return *p.value
403 }
404
405 // IsDefault returns if this is a default optional integer.
406 func (p *OptionalString) IsDefault() bool {
407 return p == nil || p.value == nil
408 }
409
410 func (p OptionalString) MarshalJSON() ([]byte, error) {
411 if p.value != nil {
412 return json.Marshal(p.value)
413 }
414 return json.Marshal(nil)
415 }
416
417 func (p *OptionalString) UnmarshalJSON(input []byte) error {
418 switch string(input) {
419 case "null", "undefined":
420 *p = OptionalString{}
421 default:
422 var value string
423 err := json.Unmarshal(input, &value)
424 if err != nil {
425 return err
426 }
427 *p = OptionalString{value: &value}
428 }
429 return nil
430 }
431
432 func (p OptionalString) String() string {
433 if p.value == nil {
434 return "default"
435 }
436 return *p.value
437 }
438
439 var (
440 _ json.Unmarshaler = (*OptionalString)(nil)
441 _ json.Marshaler = (*OptionalString)(nil)
442 )
443
444 // OptionalBytes represents a byte size that has a default value
445 //
446 // When encoded in json, Default is encoded as "null".
447 // Stores the original string representation and parses on access.
448 // Embeds OptionalString to share common functionality.
449 type OptionalBytes struct {
450 OptionalString
451 }
452
453 // NewOptionalBytes returns an OptionalBytes from a string.
454 func NewOptionalBytes(s string) *OptionalBytes {
455 return &OptionalBytes{OptionalString{value: &s}}
456 }
457
458 // IsDefault returns if this is a default optional byte value.
459 func (p *OptionalBytes) IsDefault() bool {
460 if p == nil {
461 return true
462 }
463 return p.OptionalString.IsDefault()
464 }
465
466 // WithDefault resolves the byte size with the given default.
467 // Parses the stored string value using humanize.ParseBytes.
468 func (p *OptionalBytes) WithDefault(defaultValue uint64) (value uint64) {
469 if p.IsDefault() {
470 return defaultValue
471 }
472 strValue := p.OptionalString.WithDefault("")
473 bytes, err := humanize.ParseBytes(strValue)
474 if err != nil {
475 // This should never happen as values are validated during UnmarshalJSON.
476 // If it does, it indicates either config corruption or a programming error.
477 panic(fmt.Sprintf("invalid byte size in OptionalBytes: %q - %v", strValue, err))
478 }
479 return bytes
480 }
481
482 // UnmarshalJSON validates the input is a parseable byte size.
483 func (p *OptionalBytes) UnmarshalJSON(input []byte) error {
484 switch string(input) {
485 case "null", "undefined":
486 *p = OptionalBytes{}
487 default:
488 var value any
489 err := json.Unmarshal(input, &value)
490 if err != nil {
491 return err
492 }
493 switch v := value.(type) {
494 case float64:
495 str := fmt.Sprintf("%.0f", v)
496 p.value = &str
497 case string:
498 _, err := humanize.ParseBytes(v)
499 if err != nil {
500 return err
501 }
502 p.value = &v
503 default:
504 return fmt.Errorf("unable to parse byte size, expected a size string (e.g., \"5GiB\") or a number, but got %T", v)
505 }
506 }
507 return nil
508 }
509
510 var (
511 _ json.Unmarshaler = (*OptionalBytes)(nil)
512 _ json.Marshaler = (*OptionalBytes)(nil)
513 )
514
515 type swarmLimits doNotUse
516
517 var _ json.Unmarshaler = swarmLimits(false)
518
519 func (swarmLimits) UnmarshalJSON(b []byte) error {
520 d := json.NewDecoder(bytes.NewReader(b))
521 for {
522 switch tok, err := d.Token(); err {
523 case io.EOF:
524 return nil
525 case nil:
526 switch tok {
527 case json.Delim('{'), json.Delim('}'):
528 // accept empty objects
529 continue
530 }
531 //nolint
532 return fmt.Errorf("The Swarm.ResourceMgr.Limits configuration has been removed in Kubo 0.19 and should be empty or not present. To set custom libp2p limits, read https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#user-supplied-override-limits")
533 default:
534 return err
535 }
536 }
537 }
538
539 type experimentalAcceleratedDHTClient doNotUse
540
541 var _ json.Unmarshaler = experimentalAcceleratedDHTClient(false)
542
543 func (experimentalAcceleratedDHTClient) UnmarshalJSON(b []byte) error {
544 d := json.NewDecoder(bytes.NewReader(b))
545 for {
546 switch tok, err := d.Token(); err {
547 case io.EOF:
548 return nil
549 case nil:
550 switch tok {
551 case json.Delim('{'), json.Delim('}'):
552 // accept empty objects
553 continue
554 }
555 //nolint
556 return fmt.Errorf("The Experimental.AcceleratedDHTClient key has been moved to Routing.AcceleratedDHTClient in Kubo 0.21, please use this new key and remove the old one.")
557 default:
558 return err
559 }
560 }
561 }
562
563 // doNotUse is a type you must not use, it should be struct{} but encoding/json
564 // does not support omitempty on structs and I can't be bothered to write custom
565 // marshalers on all structs that have a doNotUse field.
566 type doNotUse bool
567
568 type graphsyncEnabled doNotUse
569
570 var _ json.Unmarshaler = graphsyncEnabled(false)
571
572 func (graphsyncEnabled) UnmarshalJSON(b []byte) error {
573 d := json.NewDecoder(bytes.NewReader(b))
574 for {
575 switch tok, err := d.Token(); err {
576 case io.EOF:
577 return nil
578 case nil:
579 switch tok {
580 case json.Delim('{'), json.Delim('}'), false:
581 // accept empty objects and false
582 continue
583 }
584 //nolint
585 return fmt.Errorf("Support for Experimental.GraphsyncEnabled has been removed in Kubo 0.25.0, please remove this key. For more details see https://github.com/ipfs/kubo/pull/9747.")
586 default:
587 return err
588 }
589 }
590 }