github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/versionset/versionset.go (about) 1 package versionset 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "golang.org/x/mod/semver" 9 ) 10 11 // VersionInfo is used to negotiate between clients. 12 type VersionInfo struct { 13 Name string // abci, p2p, app, block, etc. 14 Version string // semver. 15 Optional bool // default required. 16 } 17 18 type VersionSet []VersionInfo 19 20 func (pvs VersionSet) Sort() { 21 sort.Slice(pvs, func(i, j int) bool { 22 if pvs[i].Name < pvs[j].Name { 23 return true 24 } else if pvs[i].Name == pvs[j].Name { 25 panic("should not happen") 26 } else { 27 return false 28 } 29 }) 30 } 31 32 func (pvs *VersionSet) Set(pv VersionInfo) { 33 for i, pv2 := range *pvs { 34 if pv2.Name == pv.Name { 35 (*pvs)[i] = pv 36 return 37 } 38 } 39 *pvs = append(*pvs, pv) 40 pvs.Sort() 41 return 42 } 43 44 func (pvs VersionSet) Get(name string) (pv VersionInfo, ok bool) { 45 for _, pv := range pvs { 46 if pv.Name == name { 47 return pv, true 48 } 49 } 50 ok = false 51 return 52 } 53 54 // Returns an error if not compatible. 55 // Otherwise, returns the set of compatible interfaces. 56 // Only the Major and Minor versions are returned; Patch, Prerelease, and Build 57 // portions of Semver2.0 are discarded in the resulting intersection 58 // VersionSet. 59 // TODO: test 60 func (pvs VersionSet) CompatibleWith(other VersionSet) (res VersionSet, err error) { 61 var errs []string 62 type pvpair [2]*VersionInfo 63 name2Pair := map[string]*pvpair{} 64 for _, pv := range pvs { 65 pv := pv 66 name2Pair[pv.Name] = &pvpair{&pv, nil} 67 } 68 for _, pv := range other { 69 pv := pv 70 item, ok := name2Pair[pv.Name] 71 if ok { 72 item[1] = &pv 73 } else { 74 name2Pair[pv.Name] = &pvpair{nil, &pv} 75 } 76 } 77 for _, pair := range name2Pair { 78 pv1, pv2 := pair[0], pair[1] 79 if pv1 == nil { 80 if pv2.Optional { 81 // fine 82 } else { 83 errs = append(errs, fmt.Sprintf("Other VersionSet requires %v", pv2)) 84 } 85 } else if pv2 == nil { 86 if pv1.Optional { 87 // fine 88 } else { 89 errs = append(errs, fmt.Sprintf("Our VersionSet requires %v", pv1)) 90 } 91 } else { 92 pv1mm := semver.MajorMinor(pv1.Version) 93 pv2mm := semver.MajorMinor(pv2.Version) 94 if semver.Major(pv1mm) == semver.Major(pv2mm) { 95 if semver.Compare(semver.Major(pv1mm), semver.Major(pv2mm)) > 0 { 96 res = append(res, VersionInfo{Name: pv1.Name, Version: pv2mm, Optional: pv1.Optional && pv2.Optional}) 97 } else { 98 res = append(res, VersionInfo{Name: pv1.Name, Version: pv1mm, Optional: pv1.Optional && pv2.Optional}) 99 } 100 } else { 101 errs = append(errs, fmt.Sprintf("VersionInfos not compatible: %v vs %v", pv1, pv2)) 102 } 103 } 104 } 105 if errs != nil { 106 return res, fmt.Errorf("VersionSet not compatible...\n%s", strings.Join(errs, "\n")) 107 } 108 return res, nil 109 }