gitee.com/mysnapcore/mysnapd@v0.1.0/metautil/type_conversions.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2022 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package metautil
    21  
    22  import (
    23  	"fmt"
    24  	"reflect"
    25  )
    26  
    27  func convertValue(value reflect.Value, outputType reflect.Type) (reflect.Value, error) {
    28  	inputType := value.Type()
    29  	if inputType == outputType {
    30  		return value, nil
    31  	}
    32  
    33  	var nullValue reflect.Value
    34  	switch value.Kind() {
    35  	case reflect.Slice, reflect.Array:
    36  		if outputType.Kind() != reflect.Array && outputType.Kind() != reflect.Slice {
    37  			break
    38  		}
    39  		outputValue := reflect.MakeSlice(outputType, 0, value.Len())
    40  		for i := 0; i < value.Len(); i++ {
    41  			convertedElem, err := convertValue(value.Index(i), outputType.Elem())
    42  			if err != nil {
    43  				return nullValue, err
    44  			}
    45  			outputValue = reflect.Append(outputValue, convertedElem)
    46  		}
    47  		return outputValue, nil
    48  	case reflect.Interface:
    49  		return convertValue(value.Elem(), outputType)
    50  	case reflect.Map:
    51  		if outputType.Kind() != reflect.Map {
    52  			break
    53  		}
    54  		outputValue := reflect.MakeMapWithSize(outputType, value.Len())
    55  		iter := value.MapRange()
    56  		for iter.Next() {
    57  			convertedKey, err := convertValue(iter.Key(), outputType.Key())
    58  			if err != nil {
    59  				return nullValue, err
    60  			}
    61  			convertedValue, err := convertValue(iter.Value(), outputType.Elem())
    62  			if err != nil {
    63  				return nullValue, err
    64  			}
    65  			outputValue.SetMapIndex(convertedKey, convertedValue)
    66  		}
    67  		return outputValue, nil
    68  	}
    69  	return nullValue, fmt.Errorf(`cannot convert value "%v" into a %v`, value, outputType)
    70  }
    71  
    72  // AttributeNotCompatibleError represents a type mismatch error between an interface
    73  // attribute and an expected type.
    74  type AttributeNotCompatibleError struct {
    75  	SnapName      string
    76  	InterfaceName string
    77  	AttributeName string
    78  	AttributeType reflect.Type
    79  	ExpectedType  reflect.Type
    80  }
    81  
    82  func (e AttributeNotCompatibleError) Error() string {
    83  	return fmt.Sprintf("snap %q has interface %q with invalid value type %s for %q attribute: %s", e.SnapName, e.InterfaceName, e.AttributeType, e.AttributeName, e.ExpectedType)
    84  }
    85  
    86  func (e AttributeNotCompatibleError) Is(target error) bool {
    87  	_, ok := target.(AttributeNotCompatibleError)
    88  	return ok
    89  }
    90  
    91  // SetValueFromAttribute attempts to convert the attribute value read from the
    92  // given snap/interface into the desired type.
    93  //
    94  // The snapName, ifaceName and attrName are only used to produce contextual
    95  // error messages, but are not otherwise significant. This function only
    96  // operates converting the attrVal parameter into a value which can fit into
    97  // the val parameter, which therefore must be a pointer.
    98  func SetValueFromAttribute(snapName string, ifaceName string, attrName string, attrVal interface{}, val interface{}) error {
    99  	rt := reflect.TypeOf(val)
   100  	if rt.Kind() != reflect.Ptr || val == nil {
   101  		return fmt.Errorf("internal error: cannot get %q attribute of interface %q with non-pointer value", attrName, ifaceName)
   102  	}
   103  
   104  	converted, err := convertValue(reflect.ValueOf(attrVal), rt.Elem())
   105  	if err != nil {
   106  		return AttributeNotCompatibleError{
   107  			SnapName:      snapName,
   108  			InterfaceName: ifaceName,
   109  			AttributeName: attrName,
   110  			AttributeType: reflect.TypeOf(attrVal),
   111  			ExpectedType:  reflect.TypeOf(val),
   112  		}
   113  	}
   114  	rv := reflect.ValueOf(val)
   115  	rv.Elem().Set(converted)
   116  	return nil
   117  }