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 }