summaryrefslogblamecommitdiffstats
path: root/eclass/version.eclass
blob: b0f326487e558d8da959f752e1b0a979d8601622 (plain) (tree)
































































































































































































































































                                                                                                          
# Copyright 2023
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: version.eclass
# @MAINTAINER:
# Ryan Qian <i@bitbili.net>
# @AUTHOR:
# Ryan Qian <i@bitbili.net>
# @SUPPORTED_EAPIS: 7 8
# @BLURB: functions to handle the version number
# @DESCRIPTION:
# This eclass provides functions to handle the version number.

if [[ -z ${_ECLASS_VERSION} ]]; then
_ECLASS_VERSION=1

# @ECLASS_VARIABLE: VERSION_COMPARED_PARTS
# @DESCRIPTION:
# the version parts to compare, default is the full parts,
# avaliable values are:
#   major  # major parts
#   minor  # major and minor parts
#   patch  # major, minor and patch parts
#   full   # major, minor, patch and pre-release parts
VERSION_COMPARED_PARTS="full"

# @FUNCTION: _version_compare_ver
# @INTERNAL
# @DESCRIPTION:
# _version_compare_ver sub function
_version_compare_ver() {
	debug-print-function "${FUNCNAME}" "${@}"

	local v0=${1} v1=${2} is_num=1 res=
	if [[ ! ${v0} =~ ^[0-9]+$ ]] || [[ ! ${v1} =~ ^[0-9]+$ ]]; then
		is_num=0
		: "${v0/-1/\\/}"
		: "${v1/-1/\\/}"
	fi
	if [[ ${is_num} == 1 ]]; then
		if (( ${v0} > ${v1} )); then
			res="g"
		elif (( ${v0} == ${v1} )); then
			res="e"
		elif (( ${v0} < ${v1} )); then
			res="l"
		fi
	else
		if [[ ${v0} > ${v1} ]]; then
			res="g"
		elif [[ ${v0} == ${v1} ]]; then
			res="e"
		elif [[ ${v0} < ${v1} ]]; then
			res="l"
		fi
	fi
	echo -n "${res}"
}

# @FUNCTION: _version_compare
# @INTERNAL
# @DESCRIPTION:
# _version_compare sub function
_version_compare() {
	debug-print-function "${FUNCNAME}" "${@}"

	local v0 v1 vv0 vv1 len _len res
	OIFS=$IFS; IFS="."
	v0=(${1}) v1=(${2})
	IFS=$OIFS
	if [[ ${#v0[@]} -ge ${#v1[@]} ]]; then
		len=${#v0[@]}
	else
		len=${#v1[@]}
	fi
	case $VERSION_COMPARED_PARTS in
		major)
			_len=1
			;;
		minor)
			_len=2
			;;
		patch)
			_len=3
			;;
	esac
	if [[ -n $_len && $_len -lt $len ]]; then
		len=$_len
	fi
	for (( i=0; i<${len}; i++ )); do
		vv0=${v0[$i]:--1}
		vv1=${v1[$i]:--1}
		res=$(_version_compare_ver ${vv0} ${vv1})
		if [[ ${res} != "e" ]]; then
			break
		fi
	done
	echo -n "${res}"
}

# @FUNCTION: version_compare
# @USAGE: g|ge|l|le|e STRING0 STRING1
# @DESCRIPTION:
# Compare the version numbers contained in the two strings to determine their precedence
# according to the specifications defined in https://semver.org/spec/v2.0.0.html
# The build metadata will be ignored.
#
# The prefixed package name will be removed automatically if exists.
#
# options:
#
#   g:      compare if the version contained in STRING0 is greater than the version in STRING1
#   ge:     compare if the version contained in STRING0 is greater than or equal to the version in STRING1
#   l:      compare if the version contained in STRING0 is less than the version in STRING1
#   le      compare if the version contained in STRING0 is less than or equal to the version in STRING1
#   e:      compare if the version contained in STRING0 is equal to the version in STRING1
#
# The return code is
#
#   0:      when the above comparison holds
#   1:      when the above comparison does not hold
#
version_compare() {
	debug-print-function "${FUNCNAME}" "${@}"

	local LC_ALL=C
	local action result
	case ${1} in
		g|ge|l|le|e)
			action="${1}"
			;;
		*)
			die "unexpected argument '${1}'"
			;;
	esac
	shift

	local -a str0 str1
	local str0_pkgname str0_ver str0_pre
	local str1_pkgname str1_ver str1_pre

	OIFS=$IFS; IFS="-"
	str0=( ${1} ) str1=( ${2} )
	IFS=$OIFS

	for _str in "str0" "str1"; do
		local _strA="${_str}[@]" _strV="${_str}_ver" _strP="${_str}_pre" _strN="${_str}_pkgname"
		for _s in "${!_strA}"; do
			if [[ -n ${!_strV} ]]; then
				eval "${_str}_pre=\"${_s}\""
			elif [[ ${_s} =~ ^[vV]?[0-9]+\.? ]]; then
				_s="${_s#v}"
				eval "${_str}_ver=\"${_s#V}\""
			else
				if [[ -n ${!_strN} ]]; then
					eval "${_str}_pkgname+=\"-\""
				fi
				eval "${_str}_pkgname+=\"${_s}\""
			fi
		done
	done

	if [[ ${str0_pkgname} != ${str1_pkgname} ]]; then
		die "compare with different package names '${str0_pkgname}', '${str1_pkgname}'"
	fi

	result=$(_version_compare ${str0_ver} ${str1_ver})
	if [[ ${result} == "e" ]]; then
		result=$(_version_compare ${str0_pre:-zzzz} ${str1_pre:-zzzz})
	fi

	if [[ ${result} =~ [${action}] ]]; then
		return 0
	else
		return 1
	fi
}

# @FUNCTION: version_make_range
# @USAGE: CURRENT_RANGE NEW_CONDITION
# @DESCRIPTION:
# create a version range through the NEW_CONDITION
# input format:
#   CURRENT_RANGE: ">[=] version-str <[=] version-str"
#   NEW_CONDITION: "(>|<)[=] version-str"
# output:
#   NEW_RANGE: ">[=] version-str <[=] version-str"
version_make_range() {
	debug-print-function "${FUNCNAME}" "${@}"

	if [[ -z $2 ]]; then
		die "no new condition provided"
	fi

	local oS vS oE vE o v equal replace
	read -r oS vS oE vE <<<"$1"
	read -r o v <<<"$2"
	if [[ ! $o =~ ^[\>\<]=?$ ]] || ! version_is_valid $v; then
		die "wrong format of the new condition"
	fi
	if [[ ${o:1:1} == "=" ]]; then
		equal=1
	fi
	case ${o:0:1} in
		">")
			if [[ -z $oS ]]; then
				replace=1
			elif [[ ${oS:1:1} == "=" ]] && \
				version_compare ge $v $vS; then
				replace=1
			else
				if [[ -n $equal ]] && \
					version_compare g $v $vS; then
					replace=1
				elif version_compare ge $v $vS; then
					replace=1
				fi
			fi
			if [[ -n $replace ]]; then
				oS=$o
				vS=$v
			fi
			;;
		"<")
			if [[ -z $oE ]]; then
				replace=1
			elif [[ ${oE:1:1} == "=" ]] && \
				version_compare le $v $vE; then
				replace=1
			else
				if [[ -n $equal ]] && \
					version_compare le $v $vE; then
					replace=1
				elif version_compare l $v $vE; then
					replace=1
				fi
			fi
			if [[ -n $replace ]]; then
				oE=$o
				vE=$v
			fi
			;;
	esac

	echo -n "${oS:->=} ${vS:-0} ${oE:-<=} ${vE:-9999}"
}

# @FUNCTION: version_is_valid
# @USAGE: version-str
# @DESCRIPTION:
# check the version-str is a valid semantic version
version_is_valid() {
	# TODO
	:
}

fi