code.gitea.io/gitea@v1.19.3/modules/queue/helper.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package queue 5 6 import ( 7 "reflect" 8 9 "code.gitea.io/gitea/modules/json" 10 ) 11 12 // Mappable represents an interface that can MapTo another interface 13 type Mappable interface { 14 MapTo(v interface{}) error 15 } 16 17 // toConfig will attempt to convert a given configuration cfg into the provided exemplar type. 18 // 19 // It will tolerate the cfg being passed as a []byte or string of a json representation of the 20 // exemplar or the correct type of the exemplar itself 21 func toConfig(exemplar, cfg interface{}) (interface{}, error) { 22 // First of all check if we've got the same type as the exemplar - if so it's all fine. 23 if reflect.TypeOf(cfg).AssignableTo(reflect.TypeOf(exemplar)) { 24 return cfg, nil 25 } 26 27 // Now if not - does it provide a MapTo function we can try? 28 if mappable, ok := cfg.(Mappable); ok { 29 newVal := reflect.New(reflect.TypeOf(exemplar)) 30 if err := mappable.MapTo(newVal.Interface()); err == nil { 31 return newVal.Elem().Interface(), nil 32 } 33 // MapTo has failed us ... let's try the json route ... 34 } 35 36 // OK we've been passed a byte array right? 37 configBytes, ok := cfg.([]byte) 38 if !ok { 39 // oh ... it's a string then? 40 var configStr string 41 42 configStr, ok = cfg.(string) 43 configBytes = []byte(configStr) 44 } 45 if !ok { 46 // hmm ... can we marshal it to json? 47 var err error 48 configBytes, err = json.Marshal(cfg) 49 ok = err == nil 50 } 51 if !ok { 52 // no ... we've tried hard enough at this point - throw an error! 53 return nil, ErrInvalidConfiguration{cfg: cfg} 54 } 55 56 // OK unmarshal the byte array into a new copy of the exemplar 57 newVal := reflect.New(reflect.TypeOf(exemplar)) 58 if err := json.Unmarshal(configBytes, newVal.Interface()); err != nil { 59 // If we can't unmarshal it then return an error! 60 return nil, ErrInvalidConfiguration{cfg: cfg, err: err} 61 } 62 return newVal.Elem().Interface(), nil 63 } 64 65 // unmarshalAs will attempt to unmarshal provided bytes as the provided exemplar 66 func unmarshalAs(bs []byte, exemplar interface{}) (data Data, err error) { 67 if exemplar != nil { 68 t := reflect.TypeOf(exemplar) 69 n := reflect.New(t) 70 ne := n.Elem() 71 err = json.Unmarshal(bs, ne.Addr().Interface()) 72 data = ne.Interface().(Data) 73 } else { 74 err = json.Unmarshal(bs, &data) 75 } 76 return data, err 77 } 78 79 // assignableTo will check if provided data is assignable to the same type as the exemplar 80 // if the provided exemplar is nil then it will always return true 81 func assignableTo(data Data, exemplar interface{}) bool { 82 if exemplar == nil { 83 return true 84 } 85 86 // Assert data is of same type as exemplar 87 t := reflect.TypeOf(data) 88 exemplarType := reflect.TypeOf(exemplar) 89 90 return t.AssignableTo(exemplarType) && data != nil 91 }