kubo/config/types.go
Antonio Navarro Perez d1b9e41fc2
feat: Delegated routing with custom configuration. (#9274)
New multi-router configuration system based on https://hackmd.io/G1KRDEX5T3qyfoBMkIrBew#Methods

- Added a new routing type: "custom"
- Added specific struct types for different Routers (instead of map[string]interface{})
- Added `Duration` config type, to make easier time string parsing
- Added config documentation.
- Use the latest go-delegated-routing library version with GET support.
- Added changelog notes for this feature.

It:
- closes #9157
- closes #9079
- closes #9186
2022-09-22 15:47:45 +02:00

405 lines
8.7 KiB
Go

package config
import (
"encoding/json"
"fmt"
"strings"
"time"
)
// Strings is a helper type that (un)marshals a single string to/from a single
// JSON string and a slice of strings to/from a JSON array of strings.
type Strings []string
// UnmarshalJSON conforms to the json.Unmarshaler interface.
func (o *Strings) UnmarshalJSON(data []byte) error {
if data[0] == '[' {
return json.Unmarshal(data, (*[]string)(o))
}
var value string
if err := json.Unmarshal(data, &value); err != nil {
return err
}
if len(value) == 0 {
*o = []string{}
} else {
*o = []string{value}
}
return nil
}
// MarshalJSON conforms to the json.Marshaler interface.
func (o Strings) MarshalJSON() ([]byte, error) {
switch len(o) {
case 0:
return json.Marshal(nil)
case 1:
return json.Marshal(o[0])
default:
return json.Marshal([]string(o))
}
}
var _ json.Unmarshaler = (*Strings)(nil)
var _ json.Marshaler = (*Strings)(nil)
// Flag represents a ternary value: false (-1), default (0), or true (+1).
//
// When encoded in json, False is "false", Default is "null" (or empty), and True
// is "true".
type Flag int8
const (
False Flag = -1
Default Flag = 0
True Flag = 1
)
// WithDefault resolves the value of the flag given the provided default value.
//
// Panics if Flag is an invalid value.
func (f Flag) WithDefault(defaultValue bool) bool {
switch f {
case False:
return false
case Default:
return defaultValue
case True:
return true
default:
panic(fmt.Sprintf("invalid flag value %d", f))
}
}
func (f Flag) MarshalJSON() ([]byte, error) {
switch f {
case Default:
return json.Marshal(nil)
case True:
return json.Marshal(true)
case False:
return json.Marshal(false)
default:
return nil, fmt.Errorf("invalid flag value: %d", f)
}
}
func (f *Flag) UnmarshalJSON(input []byte) error {
switch string(input) {
case "null":
*f = Default
case "false":
*f = False
case "true":
*f = True
default:
return fmt.Errorf("failed to unmarshal %q into a flag: must be null/undefined, true, or false", string(input))
}
return nil
}
func (f Flag) String() string {
switch f {
case Default:
return "default"
case True:
return "true"
case False:
return "false"
default:
return fmt.Sprintf("<invalid flag value %d>", f)
}
}
var _ json.Unmarshaler = (*Flag)(nil)
var _ json.Marshaler = (*Flag)(nil)
// Priority represents a value with a priority where 0 means "default" and -1
// means "disabled".
//
// When encoded in json, Default is encoded as "null" and Disabled is encoded as
// "false".
type Priority int64
const (
DefaultPriority Priority = 0
Disabled Priority = -1
)
// WithDefault resolves the priority with the given default.
//
// If defaultPriority is Default/0, this function will return 0.
//
// Panics if the priority has an invalid value (e.g., not DefaultPriority,
// Disabled, or > 0).
func (p Priority) WithDefault(defaultPriority Priority) (priority int64, enabled bool) {
switch p {
case Disabled:
return 0, false
case DefaultPriority:
switch defaultPriority {
case Disabled:
return 0, false
case DefaultPriority:
return 0, true
default:
if defaultPriority <= 0 {
panic(fmt.Sprintf("invalid priority %d < 0", int64(defaultPriority)))
}
return int64(defaultPriority), true
}
default:
if p <= 0 {
panic(fmt.Sprintf("invalid priority %d < 0", int64(p)))
}
return int64(p), true
}
}
func (p Priority) MarshalJSON() ([]byte, error) {
// > 0 == Priority
if p > 0 {
return json.Marshal(int64(p))
}
// <= 0 == special
switch p {
case DefaultPriority:
return json.Marshal(nil)
case Disabled:
return json.Marshal(false)
default:
return nil, fmt.Errorf("invalid priority value: %d", p)
}
}
func (p *Priority) UnmarshalJSON(input []byte) error {
switch string(input) {
case "null", "undefined":
*p = DefaultPriority
case "false":
*p = Disabled
case "true":
return fmt.Errorf("'true' is not a valid priority")
default:
var priority int64
err := json.Unmarshal(input, &priority)
if err != nil {
return err
}
if priority <= 0 {
return fmt.Errorf("priority must be positive: %d <= 0", priority)
}
*p = Priority(priority)
}
return nil
}
func (p Priority) String() string {
if p > 0 {
return fmt.Sprintf("%d", p)
}
switch p {
case DefaultPriority:
return "default"
case Disabled:
return "false"
default:
return fmt.Sprintf("<invalid priority %d>", p)
}
}
var _ json.Unmarshaler = (*Priority)(nil)
var _ json.Marshaler = (*Priority)(nil)
// OptionalDuration wraps time.Duration to provide json serialization and deserialization.
//
// NOTE: the zero value encodes to JSON nill
type OptionalDuration struct {
value *time.Duration
}
func (d *OptionalDuration) UnmarshalJSON(input []byte) error {
switch string(input) {
case "null", "undefined", "\"null\"", "", "default", "\"\"", "\"default\"":
*d = OptionalDuration{}
return nil
default:
text := strings.Trim(string(input), "\"")
value, err := time.ParseDuration(text)
if err != nil {
return err
}
*d = OptionalDuration{value: &value}
return nil
}
}
func (d *OptionalDuration) IsDefault() bool {
return d == nil || d.value == nil
}
func (d *OptionalDuration) WithDefault(defaultValue time.Duration) time.Duration {
if d == nil || d.value == nil {
return defaultValue
}
return *d.value
}
func (d OptionalDuration) MarshalJSON() ([]byte, error) {
if d.value == nil {
return json.Marshal(nil)
}
return json.Marshal(d.value.String())
}
func (d OptionalDuration) String() string {
if d.value == nil {
return "default"
}
return d.value.String()
}
var _ json.Unmarshaler = (*OptionalDuration)(nil)
var _ json.Marshaler = (*OptionalDuration)(nil)
type Duration struct {
time.Duration
}
func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
func (d *Duration) UnmarshalJSON(b []byte) error {
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
switch value := v.(type) {
case float64:
d.Duration = time.Duration(value)
return nil
case string:
var err error
d.Duration, err = time.ParseDuration(value)
if err != nil {
return err
}
return nil
default:
return fmt.Errorf("unable to parse duration, expected a duration string or a float, but got %T", v)
}
}
var _ json.Unmarshaler = (*Duration)(nil)
var _ json.Marshaler = (*Duration)(nil)
// OptionalInteger represents an integer that has a default value
//
// When encoded in json, Default is encoded as "null"
type OptionalInteger struct {
value *int64
}
// WithDefault resolves the integer with the given default.
func (p *OptionalInteger) WithDefault(defaultValue int64) (value int64) {
if p == nil || p.value == nil {
return defaultValue
}
return *p.value
}
// IsDefault returns if this is a default optional integer
func (p *OptionalInteger) IsDefault() bool {
return p == nil || p.value == nil
}
func (p OptionalInteger) MarshalJSON() ([]byte, error) {
if p.value != nil {
return json.Marshal(p.value)
}
return json.Marshal(nil)
}
func (p *OptionalInteger) UnmarshalJSON(input []byte) error {
switch string(input) {
case "null", "undefined":
*p = OptionalInteger{}
default:
var value int64
err := json.Unmarshal(input, &value)
if err != nil {
return err
}
*p = OptionalInteger{value: &value}
}
return nil
}
func (p OptionalInteger) String() string {
if p.value == nil {
return "default"
}
return fmt.Sprintf("%d", p.value)
}
var _ json.Unmarshaler = (*OptionalInteger)(nil)
var _ json.Marshaler = (*OptionalInteger)(nil)
// OptionalString represents a string that has a default value
//
// When encoded in json, Default is encoded as "null"
type OptionalString struct {
value *string
}
// NewOptionalString returns an OptionalString from a string
func NewOptionalString(s string) *OptionalString {
return &OptionalString{value: &s}
}
// WithDefault resolves the integer with the given default.
func (p *OptionalString) WithDefault(defaultValue string) (value string) {
if p == nil || p.value == nil {
return defaultValue
}
return *p.value
}
// IsDefault returns if this is a default optional integer
func (p *OptionalString) IsDefault() bool {
return p == nil || p.value == nil
}
func (p OptionalString) MarshalJSON() ([]byte, error) {
if p.value != nil {
return json.Marshal(p.value)
}
return json.Marshal(nil)
}
func (p *OptionalString) UnmarshalJSON(input []byte) error {
switch string(input) {
case "null", "undefined":
*p = OptionalString{}
default:
var value string
err := json.Unmarshal(input, &value)
if err != nil {
return err
}
*p = OptionalString{value: &value}
}
return nil
}
func (p OptionalString) String() string {
if p.value == nil {
return "default"
}
return *p.value
}
var _ json.Unmarshaler = (*OptionalInteger)(nil)
var _ json.Marshaler = (*OptionalInteger)(nil)