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  }