From 99066209a23a7742f401db1c8b10560320211385 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 27 Nov 2025 13:59:42 +0100 Subject: [PATCH] libnetwork/options: GenerateFromModel: use generics Use generics so that the produced output is already in the right type. Signed-off-by: Sebastiaan van Stijn --- .../libnetwork/drivers/bridge/bridge_linux.go | 6 +----- daemon/libnetwork/options/options.go | 18 ++++++++++-------- daemon/libnetwork/options/options_test.go | 10 +++++----- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/daemon/libnetwork/drivers/bridge/bridge_linux.go b/daemon/libnetwork/drivers/bridge/bridge_linux.go index 8a5a8ff4be..267f23c329 100644 --- a/daemon/libnetwork/drivers/bridge/bridge_linux.go +++ b/daemon/libnetwork/drivers/bridge/bridge_linux.go @@ -1865,11 +1865,7 @@ func parseContainerOptions(cOptions map[string]any) (*containerConfiguration, er } switch opt := genericData.(type) { case options.Generic: - opaqueConfig, err := options.GenerateFromModel(opt, &containerConfiguration{}) - if err != nil { - return nil, err - } - return opaqueConfig.(*containerConfiguration), nil + return options.GenerateFromModel[*containerConfiguration](opt) case *containerConfiguration: return opt, nil default: diff --git a/daemon/libnetwork/options/options.go b/daemon/libnetwork/options/options.go index 38d5a78181..19d47c15fd 100644 --- a/daemon/libnetwork/options/options.go +++ b/daemon/libnetwork/options/options.go @@ -51,10 +51,12 @@ type Generic map[string]any // // The return value is of the same type than the model (including a potential // pointer qualifier). -func GenerateFromModel(options Generic, model any) (any, error) { - modType := reflect.TypeOf(model) +func GenerateFromModel[T any](options Generic) (T, error) { + var zero T + + modType := reflect.TypeFor[T]() if modType == nil { - return nil, errors.New("invalid model: model is nil") + return zero, errors.New("invalid model: model is nil") } isPtr := modType.Kind() == reflect.Ptr @@ -72,21 +74,21 @@ func GenerateFromModel(options Generic, model any) (any, error) { for name, value := range options { field := resVal.FieldByName(name) if !field.IsValid() { - return nil, NoSuchFieldError{Field: name, Type: resType.String()} + return zero, NoSuchFieldError{Field: name, Type: resType.String()} } if !field.CanSet() { - return nil, CannotSetFieldError{Field: name, Type: resType.String()} + return zero, CannotSetFieldError{Field: name, Type: resType.String()} } val := reflect.ValueOf(value) if val.Type() != field.Type() { - return nil, TypeMismatchError{Field: name, ExpectType: field.Type().String(), ActualType: val.Type().String()} + return zero, TypeMismatchError{Field: name, ExpectType: field.Type().String(), ActualType: val.Type().String()} } field.Set(val) } // If the model is not of pointer type, return content of the result. if isPtr { - return res.Interface(), nil + return res.Interface().(T), nil } - return resVal.Interface(), nil + return resVal.Interface().(T), nil } diff --git a/daemon/libnetwork/options/options_test.go b/daemon/libnetwork/options/options_test.go index 4ce8ee3954..daa48ae713 100644 --- a/daemon/libnetwork/options/options_test.go +++ b/daemon/libnetwork/options/options_test.go @@ -25,7 +25,7 @@ func TestGenerate(t *testing.T) { Rune: 'b', Float64: 2.0, } - result, err := GenerateFromModel(gen, Model{}) + result, err := GenerateFromModel[Model](gen) assert.Check(t, err) assert.Check(t, is.DeepEqual(result, expected)) } @@ -49,7 +49,7 @@ func TestGeneratePtr(t *testing.T) { Float64: 2.0, } - result, err := GenerateFromModel(gen, &Model{}) + result, err := GenerateFromModel[*Model](gen) assert.NilError(t, err) assert.Check(t, is.DeepEqual(result, expected)) } @@ -57,7 +57,7 @@ func TestGeneratePtr(t *testing.T) { func TestGenerateMissingField(t *testing.T) { type Model struct{} gen := Generic{"foo": "bar"} - _, err := GenerateFromModel(gen, Model{}) + _, err := GenerateFromModel[Model](gen) const expected = `no field "foo" in type "options.Model"` assert.Check(t, is.Error(err, expected)) assert.Check(t, is.ErrorType(err, NoSuchFieldError{})) @@ -68,7 +68,7 @@ func TestFieldCannotBeSet(t *testing.T) { foo int //nolint:nolintlint,unused // un-exported field is used to test error-handling } gen := Generic{"foo": "bar"} - _, err := GenerateFromModel(gen, Model{}) + _, err := GenerateFromModel[Model](gen) const expected = `cannot set field "foo" of type "options.Model"` assert.Check(t, is.Error(err, expected)) assert.Check(t, is.ErrorType(err, CannotSetFieldError{})) @@ -79,7 +79,7 @@ func TestTypeMismatchError(t *testing.T) { Foo int } gen := Generic{"Foo": "bar"} - _, err := GenerateFromModel(gen, Model{}) + _, err := GenerateFromModel[Model](gen) const expected = `type mismatch, field Foo require type int, actual type string` assert.Check(t, is.Error(err, expected)) assert.Check(t, is.ErrorType(err, TypeMismatchError{}))