github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/base/ver/ver.go (about)

     1  package ver
     2  
     3  import (
     4  	"bytes"
     5  	"database/sql/driver"
     6  	"errors"
     7  	"fmt"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  )
    12  
    13  var (
    14  	ErrInvalidSemVer     = errors.New("invalid semantic version")
    15  	ErrInvalidMetadata   = errors.New("invalid Metadata string")
    16  	ErrInvalidPrerelease = errors.New("invalid Prerelease string")
    17  )
    18  
    19  type Version struct {
    20  	major         uint64
    21  	minor         uint64
    22  	patch         uint64
    23  	prerelease    string
    24  	buildMetadata string
    25  }
    26  
    27  var (
    28  	regexpVerCore          = regexp.MustCompile(`(?P<major>0|[1-9]\d*)(\.(?P<minor>0|[1-9]\d*))?(\.(?P<patch>0|[1-9]\d*))?`)
    29  	regexpVerPrerelease    = regexp.MustCompile(`(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)`)
    30  	regexpVerBuildMetadata = regexp.MustCompile(`(?P<buildMetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)`)
    31  	regexpVersion          = regexp.MustCompile(`^v?` + regexpVerCore.String() + `(?:-` + regexpVerPrerelease.String() + `)?(?:\+` + regexpVerBuildMetadata.String() + `)?$`)
    32  )
    33  
    34  func ParseVersion(v string) (*Version, error) {
    35  	if !regexpVersion.MatchString(v) {
    36  		return nil, ErrInvalidSemVer
    37  	}
    38  
    39  	matched := regexpVersion.FindAllStringSubmatch(v, -1)[0]
    40  
    41  	ver := Version{}
    42  
    43  	for i, name := range regexpVersion.SubexpNames() {
    44  		v := matched[i]
    45  
    46  		switch name {
    47  		case "major":
    48  			ver.major, _ = strconv.ParseUint(v, 10, 64)
    49  		case "minor":
    50  			ver.minor, _ = strconv.ParseUint(v, 10, 64)
    51  		case "patch":
    52  			ver.patch, _ = strconv.ParseUint(v, 10, 64)
    53  		case "prerelease":
    54  			ver.prerelease = v
    55  		case "buildMetadata":
    56  			ver.buildMetadata = v
    57  		}
    58  	}
    59  
    60  	return &ver, nil
    61  }
    62  
    63  func MustParseVersion(v string) *Version {
    64  	sv, err := ParseVersion(v)
    65  	if err != nil {
    66  		panic(err)
    67  	}
    68  	return sv
    69  }
    70  
    71  func (v Version) String() string {
    72  	buf := bytes.NewBuffer(nil)
    73  
    74  	fmt.Fprintf(buf, "%d.%d.%d", v.major, v.minor, v.patch)
    75  
    76  	if v.prerelease != "" {
    77  		fmt.Fprintf(buf, "-%s", v.prerelease)
    78  	}
    79  	if v.buildMetadata != "" {
    80  		fmt.Fprintf(buf, "+%s", v.buildMetadata)
    81  	}
    82  
    83  	return buf.String()
    84  }
    85  
    86  func (v Version) Major() uint64 {
    87  	return v.major
    88  }
    89  
    90  func (v Version) Minor() uint64 {
    91  	return v.minor
    92  }
    93  
    94  func (v Version) Patch() uint64 {
    95  	return v.patch
    96  }
    97  
    98  func (v Version) Prerelease() string {
    99  	return v.prerelease
   100  }
   101  
   102  func (v Version) Metadata() string {
   103  	return v.buildMetadata
   104  }
   105  
   106  func (v Version) IncrPatch() *Version {
   107  	if v.prerelease != "" {
   108  		v.buildMetadata = ""
   109  		v.prerelease = ""
   110  	} else {
   111  		v.buildMetadata = ""
   112  		v.prerelease = ""
   113  		v.patch = v.patch + 1
   114  	}
   115  	return &v
   116  }
   117  
   118  func (v Version) IncrMinor() *Version {
   119  	v.buildMetadata = ""
   120  	v.prerelease = ""
   121  	v.patch = 0
   122  	v.minor = v.minor + 1
   123  	return &v
   124  }
   125  
   126  func (v Version) IncrMajor() *Version {
   127  	v.buildMetadata = ""
   128  	v.prerelease = ""
   129  	v.patch = 0
   130  	v.minor = 0
   131  	v.major = v.major + 1
   132  	return &v
   133  }
   134  
   135  func (v Version) WithPrerelease(prerelease string) (*Version, error) {
   136  	if len(prerelease) > 0 {
   137  		if !regexpVerPrerelease.MatchString(prerelease) {
   138  			return nil, ErrInvalidPrerelease
   139  		}
   140  		v.prerelease = prerelease
   141  	}
   142  	return &v, nil
   143  }
   144  
   145  func (v Version) WithBuildMetadata(buildMetadata string) (*Version, error) {
   146  	if len(buildMetadata) > 0 {
   147  		if !regexpVerBuildMetadata.MatchString(buildMetadata) {
   148  			return nil, ErrInvalidMetadata
   149  		}
   150  		v.buildMetadata = buildMetadata
   151  	}
   152  	return &v, nil
   153  }
   154  
   155  func (v *Version) LessThan(o *Version) bool {
   156  	return v.Compare(o) < 0
   157  }
   158  
   159  func (v *Version) GreaterThan(o *Version) bool {
   160  	return v.Compare(o) > 0
   161  }
   162  
   163  func (v *Version) Equal(o *Version) bool {
   164  	return v.Compare(o) == 0
   165  }
   166  
   167  func (v *Version) Compare(o *Version) int {
   168  	if d := compareSegment(v.Major(), o.Major()); d != 0 {
   169  		return d
   170  	}
   171  	if d := compareSegment(v.Minor(), o.Minor()); d != 0 {
   172  		return d
   173  	}
   174  	if d := compareSegment(v.Patch(), o.Patch()); d != 0 {
   175  		return d
   176  	}
   177  
   178  	ps := v.prerelease
   179  	po := o.Prerelease()
   180  
   181  	if ps == "" && po == "" {
   182  		return 0
   183  	}
   184  	if ps == "" {
   185  		return 1
   186  	}
   187  	if po == "" {
   188  		return -1
   189  	}
   190  
   191  	return comparePrerelease(ps, po)
   192  }
   193  
   194  func (v *Version) UnmarshalText(b []byte) error {
   195  	ver, err := ParseVersion(string(b))
   196  	if err != nil {
   197  		return err
   198  	}
   199  	*v = *ver
   200  	return nil
   201  }
   202  
   203  func (v Version) MarshalText() ([]byte, error) {
   204  	return []byte(v.String()), nil
   205  }
   206  
   207  func (v *Version) UnmarshalJSON(b []byte) error { return v.UnmarshalText(b) }
   208  
   209  func (v Version) MarshalJSON() ([]byte, error) { return v.MarshalText() }
   210  
   211  func (v *Version) DataType(driver string) string {
   212  	return "varchar"
   213  }
   214  
   215  func (v *Version) Scan(value interface{}) error {
   216  	var s string
   217  	s, _ = value.(string)
   218  	ver, err := ParseVersion(s)
   219  	if err != nil {
   220  		return err
   221  	}
   222  	*v = *ver
   223  	return nil
   224  }
   225  
   226  func (v Version) Value() (driver.Value, error) {
   227  	return v.String(), nil
   228  }
   229  
   230  func compareSegment(v, o uint64) int {
   231  	if v < o {
   232  		return -1
   233  	}
   234  	if v > o {
   235  		return 1
   236  	}
   237  	return 0
   238  }
   239  
   240  func comparePrerelease(v, o string) int {
   241  	sparts := strings.Split(v, ".")
   242  	oparts := strings.Split(o, ".")
   243  
   244  	slen := len(sparts)
   245  	olen := len(oparts)
   246  
   247  	l := slen
   248  	if olen > slen {
   249  		l = olen
   250  	}
   251  
   252  	for i := 0; i < l; i++ {
   253  		tmpStr := ""
   254  		if i < slen {
   255  			tmpStr = sparts[i]
   256  		}
   257  
   258  		otemp := ""
   259  		if i < olen {
   260  			otemp = oparts[i]
   261  		}
   262  
   263  		d := comparePrePart(tmpStr, otemp)
   264  		if d != 0 {
   265  			return d
   266  		}
   267  	}
   268  
   269  	return 0
   270  }
   271  
   272  func comparePrePart(s, o string) int {
   273  	if s == o {
   274  		return 0
   275  	}
   276  
   277  	if s == "" {
   278  		if o != "" {
   279  			return -1
   280  		}
   281  		return 1
   282  	}
   283  
   284  	if o == "" {
   285  		if s != "" {
   286  			return 1
   287  		}
   288  		return -1
   289  	}
   290  
   291  	oi, n1 := strconv.ParseUint(o, 10, 64)
   292  	si, n2 := strconv.ParseUint(s, 10, 64)
   293  
   294  	if n1 != nil && n2 != nil {
   295  		if s > o {
   296  			return 1
   297  		}
   298  		return -1
   299  	} else if n1 != nil {
   300  		return -1
   301  	} else if n2 != nil {
   302  		return 1
   303  	}
   304  	if si > oi {
   305  		return 1
   306  	}
   307  	return -1
   308  
   309  }