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 }