// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This is a fork of internal/gover for use by x/tools until
// go1.21 and earlier are no longer supported by x/tools.

package versions

import 

// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
// The numbers are the original decimal strings to avoid integer overflows
// and since there is very little actual math. (Probably overflow doesn't matter in practice,
// but at the time this code was written, there was an existing test that used
// go1.99999999999, which does not fit in an int on 32-bit platforms.
// The "big decimal" representation avoids the problem entirely.)
type gover struct {
	major string // decimal
	minor string // decimal or ""
	patch string // decimal or ""
	kind  string // "", "alpha", "beta", "rc"
	pre   string // decimal or ""
}

// compare returns -1, 0, or +1 depending on whether
// x < y, x == y, or x > y, interpreted as toolchain versions.
// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
// Malformed versions compare less than well-formed versions and equal to each other.
// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
func (,  string) int {
	 := parse()
	 := parse()

	if  := cmpInt(.major, .major);  != 0 {
		return 
	}
	if  := cmpInt(.minor, .minor);  != 0 {
		return 
	}
	if  := cmpInt(.patch, .patch);  != 0 {
		return 
	}
	if  := strings.Compare(.kind, .kind);  != 0 { // "" < alpha < beta < rc
		return 
	}
	if  := cmpInt(.pre, .pre);  != 0 {
		return 
	}
	return 0
}

// lang returns the Go language version. For example, lang("1.2.3") == "1.2".
func ( string) string {
	 := parse()
	if .minor == "" || .major == "1" && .minor == "0" {
		return .major
	}
	return .major + "." + .minor
}

// isValid reports whether the version x is valid.
func ( string) bool {
	return parse() != gover{}
}

// parse parses the Go version string x into a version.
// It returns the zero version if x is malformed.
func ( string) gover {
	var  gover

	// Parse major version.
	var  bool
	.major, ,  = cutInt()
	if ! {
		return gover{}
	}
	if  == "" {
		// Interpret "1" as "1.0.0".
		.minor = "0"
		.patch = "0"
		return 
	}

	// Parse . before minor version.
	if [0] != '.' {
		return gover{}
	}

	// Parse minor version.
	.minor, ,  = cutInt([1:])
	if ! {
		return gover{}
	}
	if  == "" {
		// Patch missing is same as "0" for older versions.
		// Starting in Go 1.21, patch missing is different from explicit .0.
		if cmpInt(.minor, "21") < 0 {
			.patch = "0"
		}
		return 
	}

	// Parse patch if present.
	if [0] == '.' {
		.patch, ,  = cutInt([1:])
		if ! ||  != "" {
			// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
			// Allowing them would be a bit confusing because we already have:
			//	1.21 < 1.21rc1
			// But a prerelease of a patch would have the opposite effect:
			//	1.21.3rc1 < 1.21.3
			// We've never needed them before, so let's not start now.
			return gover{}
		}
		return 
	}

	// Parse prerelease.
	 := 0
	for  < len() && ([] < '0' || '9' < []) {
		if [] < 'a' || 'z' < [] {
			return gover{}
		}
		++
	}
	if  == 0 {
		return gover{}
	}
	.kind,  = [:], [:]
	if  == "" {
		return 
	}
	.pre, ,  = cutInt()
	if ! ||  != "" {
		return gover{}
	}

	return 
}

// cutInt scans the leading decimal number at the start of x to an integer
// and returns that value and the rest of the string.
func ( string) (,  string,  bool) {
	 := 0
	for  < len() && '0' <= [] && [] <= '9' {
		++
	}
	if  == 0 || [0] == '0' &&  != 1 { // no digits or unnecessary leading zero
		return "", "", false
	}
	return [:], [:], true
}

// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
// (Copied from golang.org/x/mod/semver's compareInt.)
func (,  string) int {
	if  ==  {
		return 0
	}
	if len() < len() {
		return -1
	}
	if len() > len() {
		return +1
	}
	if  <  {
		return -1
	} else {
		return +1
	}
}