diff --git a/config/internal.go b/config/internal.go index 2e71ac16eb7..318bb339232 100644 --- a/config/internal.go +++ b/config/internal.go @@ -1,7 +1,8 @@ package config type Internal struct { - Bitswap *InternalBitswap `json:",omitempty"` // This is omitempty since we are expecting to make changes to all subcomponents of Internal + Bitswap *InternalBitswap `json:",omitempty"` // This is omitempty since we are expecting to make changes to all subcomponents of Internal + UnixFSShardingSizeThreshold *OptionalString `json:",omitempty"` } type InternalBitswap struct { diff --git a/config/types.go b/config/types.go index ac90fa9b82a..baa073ab460 100644 --- a/config/types.go +++ b/config/types.go @@ -313,3 +313,55 @@ func (p OptionalInteger) String() string { 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 +} + +// 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 fmt.Sprintf("%d", p.value) +} + +var _ json.Unmarshaler = (*OptionalInteger)(nil) +var _ json.Marshaler = (*OptionalInteger)(nil) diff --git a/config/types_test.go b/config/types_test.go index 06ea73a260c..2477f8bcf9a 100644 --- a/config/types_test.go +++ b/config/types_test.go @@ -389,10 +389,106 @@ func TestOptionalInteger(t *testing.T) { for _, invalid := range []string{ "foo", "-1.1", "1.1", "0.0", "[]", } { - var p Priority + var p OptionalInteger err := json.Unmarshal([]byte(invalid), &p) if err == nil { t.Errorf("expected to fail to decode %s as a priority", invalid) } } } + +func TestOptionalString(t *testing.T) { + makeStringPointer := func(v string) *string { + return &v + } + + var defaultOptionalString OptionalString + if !defaultOptionalString.IsDefault() { + t.Fatal("should be the default") + } + if val := defaultOptionalString.WithDefault(""); val != "" { + t.Errorf("optional integer should have been empty, got %s", val) + } + + if val := defaultOptionalString.WithDefault("foo"); val != "foo" { + t.Errorf("optional integer should have been foo, got %s", val) + } + + var filledStr OptionalString + filledStr = OptionalString{value: makeStringPointer("foo")} + if filledStr.IsDefault() { + t.Fatal("should not be the default") + } + if val := filledStr.WithDefault("bar"); val != "foo" { + t.Errorf("optional integer should have been foo, got %s", val) + } + + filledStr = OptionalString{value: makeStringPointer("")} + if val := filledStr.WithDefault("foo"); val != "" { + t.Errorf("optional integer should have been 0, got %s", val) + } + + for jsonStr, goValue := range map[string]OptionalString{ + "null": {}, + "\"0\"": {value: makeStringPointer("0")}, + `"1"`: {value: makeStringPointer("1")}, + `"-1"`: {value: makeStringPointer("-1")}, + `"qwerty"`: {value: makeStringPointer("qwerty")}, + } { + var d OptionalString + err := json.Unmarshal([]byte(jsonStr), &d) + if err != nil { + t.Fatal(err) + } + + if goValue.value == nil && d.value == nil { + } else if goValue.value == nil && d.value != nil { + t.Errorf("expected default, got %s", d) + } else if *d.value != *goValue.value { + t.Fatalf("expected %s, got %s", goValue, d) + } + + // Reverse + out, err := json.Marshal(goValue) + if err != nil { + t.Fatal(err) + } + if string(out) != jsonStr { + t.Fatalf("expected %s, got %s", jsonStr, string(out)) + } + } + + // marshal with omitempty + type Foo struct { + S *OptionalString `json:",omitempty"` + } + out, err := json.Marshal(new(Foo)) + if err != nil { + t.Fatal(err) + } + expected := "{}" + if string(out) != expected { + t.Fatal("expected omitempty to omit the optional integer") + } + // unmarshal from omitempty output and get default value + var foo2 Foo + if err := json.Unmarshal(out, &foo2); err != nil { + t.Fatalf("%s failed to unmarshall with %s", string(out), err) + } + if s := foo2.S.WithDefault("foo"); s != "foo" { + t.Fatalf("expected default value to be used, got %s", s) + } + if !foo2.S.IsDefault() { + t.Fatal("expected value to be the default") + } + + for _, invalid := range []string{ + "[]", "{}", "0", "a", "'b'", + } { + var p OptionalString + err := json.Unmarshal([]byte(invalid), &p) + if err == nil { + t.Errorf("expected to fail to decode %s as an optional string", invalid) + } + } +}