github.com/moby/docker@v26.1.3+incompatible/libnetwork/options/options.go (about)

     1  // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
     2  //go:build go1.19
     3  
     4  // Package options provides a way to pass unstructured sets of options to a
     5  // component expecting a strongly-typed configuration structure.
     6  package options
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  )
    12  
    13  // NoSuchFieldError is the error returned when the generic parameters hold a
    14  // value for a field absent from the destination structure.
    15  type NoSuchFieldError struct {
    16  	Field string
    17  	Type  string
    18  }
    19  
    20  func (e NoSuchFieldError) Error() string {
    21  	return fmt.Sprintf("no field %q in type %q", e.Field, e.Type)
    22  }
    23  
    24  // CannotSetFieldError is the error returned when the generic parameters hold a
    25  // value for a field that cannot be set in the destination structure.
    26  type CannotSetFieldError struct {
    27  	Field string
    28  	Type  string
    29  }
    30  
    31  func (e CannotSetFieldError) Error() string {
    32  	return fmt.Sprintf("cannot set field %q of type %q", e.Field, e.Type)
    33  }
    34  
    35  // TypeMismatchError is the error returned when the type of the generic value
    36  // for a field mismatches the type of the destination structure.
    37  type TypeMismatchError struct {
    38  	Field      string
    39  	ExpectType string
    40  	ActualType string
    41  }
    42  
    43  func (e TypeMismatchError) Error() string {
    44  	return fmt.Sprintf("type mismatch, field %s require type %v, actual type %v", e.Field, e.ExpectType, e.ActualType)
    45  }
    46  
    47  // Generic is a basic type to store arbitrary settings.
    48  type Generic map[string]any
    49  
    50  // GenerateFromModel takes the generic options, and tries to build a new
    51  // instance of the model's type by matching keys from the generic options to
    52  // fields in the model.
    53  //
    54  // The return value is of the same type than the model (including a potential
    55  // pointer qualifier).
    56  func GenerateFromModel(options Generic, model interface{}) (interface{}, error) {
    57  	modType := reflect.TypeOf(model)
    58  
    59  	// If the model is of pointer type, we need to dereference for New.
    60  	resType := reflect.TypeOf(model)
    61  	if modType.Kind() == reflect.Ptr {
    62  		resType = resType.Elem()
    63  	}
    64  
    65  	// Populate the result structure with the generic layout content.
    66  	res := reflect.New(resType)
    67  	for name, value := range options {
    68  		field := res.Elem().FieldByName(name)
    69  		if !field.IsValid() {
    70  			return nil, NoSuchFieldError{name, resType.String()}
    71  		}
    72  		if !field.CanSet() {
    73  			return nil, CannotSetFieldError{name, resType.String()}
    74  		}
    75  		if reflect.TypeOf(value) != field.Type() {
    76  			return nil, TypeMismatchError{name, field.Type().String(), reflect.TypeOf(value).String()}
    77  		}
    78  		field.Set(reflect.ValueOf(value))
    79  	}
    80  
    81  	// If the model is not of pointer type, return content of the result.
    82  	if modType.Kind() == reflect.Ptr {
    83  		return res.Interface(), nil
    84  	}
    85  	return res.Elem().Interface(), nil
    86  }