func SemverEqMatcher(condition entities.Condition, user entities.UserContext, logger logging.OptimizelyLogProducer) (bool, error) {
	comparison, err := SemverEvaluator(condition, user)
	if err != nil {
		return false, err
	}
	return comparison == 0, nil
}
SemverEqMatcher returns true if the user's semver attribute is equal to the semver condition value
package matchers
import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"github.com/optimizely/go-sdk/pkg/logging"
	pkgReasons "github.com/optimizely/go-sdk/pkg/decision/reasons"
	"github.com/optimizely/go-sdk/pkg/entities"
	"github.com/pkg/errors"
)
const (
	buildSeperator      = "+"
	preReleaseSeperator = "-"
	whiteSpace          = " "
)
var digitCheck = regexp.MustCompile(`^[0-9]+$`)
type SemanticVersion struct {
	Condition string  
}
func (sv SemanticVersion) compareVersion(attribute string) (int, error) {
	targetedVersionParts, err := sv.splitSemanticVersion(sv.Condition)
	if err != nil {
		return 0, err
	}
	versionParts, e := sv.splitSemanticVersion(attribute)
	if e != nil {
		return 0, e
	}
	for idx := range targetedVersionParts {
		switch {
		case len(versionParts) <= idx:
			if sv.isPreRelease(attribute) {
				return 1, nil
			}
			return -1, nil
		case !sv.isNumber(versionParts[idx]):
			if result := sv.compareVersionStrings(versionParts[idx], targetedVersionParts[idx], attribute); result != 0 {
				return result, nil
			}
		case sv.isNumber(targetedVersionParts[idx]):  
			if result := sv.compareVersionNumbers(versionParts[idx], targetedVersionParts[idx]); result != 0 {
				return result, nil
			}
		default:
			return -1, nil
		}
	}
	if sv.isPreRelease(attribute) && !sv.isPreRelease(sv.Condition) {
		return -1, nil
	}
	return 0, nil
}
func (sv SemanticVersion) splitSemanticVersion(targetedVersion string) ([]string, error) {
	if sv.hasWhiteSpace(targetedVersion) {
		return []string{}, errors.New(string(pkgReasons.AttributeFormatInvalid))
	}
	targetPrefix := targetedVersion
	var targetSuffix []string
	if sv.isPreRelease(targetedVersion) || sv.isBuild(targetedVersion) {
		sep := buildSeperator
		if sv.isPreRelease(targetedVersion) {
			sep = preReleaseSeperator
		}
		targetParts := strings.SplitN(targetedVersion, sep, 2)
		if len(targetParts) == 0 {
			return []string{}, errors.New(string(pkgReasons.AttributeFormatInvalid))
		}
		targetPrefix = targetParts[0]
		targetSuffix = targetParts[1:]
	}
	targetedVersionParts := strings.Split(targetPrefix, ".")
	if len(targetedVersionParts) > 3 {
		return []string{}, errors.New(string(pkgReasons.AttributeFormatInvalid))
	}
	if len(targetedVersionParts) == 0 {
		return []string{}, errors.New(string(pkgReasons.AttributeFormatInvalid))
	}
	for i := 0; i < len(targetedVersionParts); i++ {
		if !sv.isNumber(targetedVersionParts[i]) {
			return []string{}, errors.New(string(pkgReasons.AttributeFormatInvalid))
		}
	}
	targetedVersionParts = append(targetedVersionParts, targetSuffix...)
	return targetedVersionParts, nil
}
func (sv SemanticVersion) compareVersionStrings(versionPart, targetedVersionPart, attribute string) int {
	if versionPart < targetedVersionPart {
		if sv.isPreRelease(sv.Condition) && !sv.isPreRelease(attribute) {
			return 1
		}
		return -1
	} else if versionPart > targetedVersionPart {
		if !sv.isPreRelease(sv.Condition) && sv.isPreRelease(attribute) {
			return -1
		}
		return 1
	}
	return 0
}
func (sv SemanticVersion) compareVersionNumbers(versionPart, targetedVersionPart string) int {
	if sv.toInt(versionPart) < sv.toInt(targetedVersionPart) {
		return -1
	} else if sv.toInt(versionPart) > sv.toInt(targetedVersionPart) {
		return 1
	}
	return 0
}
func (sv SemanticVersion) isNumber(str string) bool {
	return digitCheck.MatchString(str)
}
func (sv SemanticVersion) toInt(str string) int {
	i, e := strconv.Atoi(str)
	if e != nil {
		return 0
	}
	return i
}
func (sv SemanticVersion) isPreRelease(str string) bool {
	if prIndex := strings.Index(str, preReleaseSeperator); prIndex > 0 {
		if buildIndex := strings.Index(str, buildSeperator); buildIndex > 0 {
			return prIndex < buildIndex
		}
		return true
	}
	return false
}
func (sv SemanticVersion) isBuild(str string) bool {
	if buildIndex := strings.Index(str, buildSeperator); buildIndex > 0 {
		if prIndex := strings.Index(str, preReleaseSeperator); prIndex > 0 {
			return buildIndex < prIndex
		}
		return true
	}
	return false
}
func (sv SemanticVersion) hasWhiteSpace(str string) bool {
	return str == "" || strings.Contains(str, whiteSpace)
}
func SemverEvaluator(cond entities.Condition, user entities.UserContext) (int, error) {
	if stringValue, ok := cond.Value.(string); ok {
		attributeValue, err := user.GetStringAttribute(cond.Name)
		if err != nil {
			return 0, err
		}
		semVer := SemanticVersion{stringValue}
		comparison, e := semVer.compareVersion(attributeValue)
		if e != nil {
			return 0, e
		}
		return comparison, nil
	}
	return 0, fmt.Errorf("audience condition %s evaluated to NULL because the condition value type is not supported", cond.Name)
}