Skip to content

Instantly share code, notes, and snippets.

@a-ludi
Created November 10, 2021 10:21
Show Gist options
  • Save a-ludi/2c7c1dfd477aa38a2097d38072cd806f to your computer and use it in GitHub Desktop.
Save a-ludi/2c7c1dfd477aa38a2097d38072cd806f to your computer and use it in GitHub Desktop.
Compare a version against a set of match clauses
# Usage: version_matches <version> <clause>
#
# Params:
# <version> Dot-separated version specifier, e.g. 2.4.32
# <clause> Match-clause according to grammer (below), e.g. >=1.0.0&<2.0.0
#
# Grammar:
#
# <version> ::= <uint> | <uint> "." <version>
# <version_pred> ::= <version> | <op> <version>
# <op> ::= "<" | "<=" | "!=" | "==" | ">=" | ">"
# <clause> ::= <version_pred> | <and_clause> | <or_clause>
# <or_clause> ::= <and_clause> | <and_clause> "," <or_clause>
# <and_clause> ::= <version_pred> | <version_pred> "&" <and_clause>
#
# Examples:
#
# version_matches 1.2.3 '==1.2.3'
# version_matches 1.2.3 '>=1.2.3'
# version_matches 1.2.3 '<=1.2.3'
# ! version_matches 1.2.3 '!=1.2.3'
# ! version_matches 1.2.3 '<1.2.3'
# ! version_matches 1.2.3 '<1.2.3'
# version_matches 2 '==2.0.0'
# version_matches 2.31 '>=2.0.0&<3.0.0'
# ! version_matches 2.31 '>=2.0.0&<3.0.0&!=2.31.0'
# version_matches 2.31 '>=2.0.0&<3.0.0&!=2.31.0,2.31.0'
#
# Author: Arne Ludwig <arne.ludwig@posteo.de>
# Copyright: © 2021 Arne Ludwig
# License: MIT
version_matches() {
local VERSION="$1"
local CLAUSE="$2"
if [[ -z "$CLAUSE" ]]
then
return
elif [[ -z "${CLAUSE##*,*}" ]]
then
while read -rd ',' COND
do
if version_matches "$VERSION" "$COND"
then
return 0
fi
done <<<"$CLAUSE,"
elif [[ -z "${CLAUSE##*&*}" ]]
then
while read -rd '&' COND
do
version_matches "$VERSION" "$COND" || return 1
done <<<"$CLAUSE&"
else
case "$CLAUSE" in
\<=*) OP='<=';;
\<*) OP='<';;
\>=*) OP='>=';;
\>*) OP='>';;
!=*) OP='!=';;
==*) OP='==';;
[0-9]*) OP='==';;
*)
echo "error: illegal version clause: $CLAUSE"
return 2
;;
esac
local TARGET="${CLAUSE#$OP}"
IFS='.' read -ra version <<<"$VERSION."
# echo "VERSION=$VERSION version=( ${version[*]} )"
IFS='.' read -ra target <<<"$TARGET."
# echo "TARGET=$TARGET target=( ${target[*]} )"
CMP=0
for (( i = 0; i < ${#version[*]} || i < ${#target[*]}; ++i ))
do
(( i < ${#version[*]} )) || version[$i]=0
(( i < ${#target[*]} )) || target[$i]=0
if (( "${version[i]}" < "${target[i]}" )); then
CMP=-1
break
elif (( "${version[i]}" > "${target[i]}" )); then
# shellcheck disable=SC2034
CMP=1
break
fi
done
(( CMP != 0 )) || (( CMP = ${#version[*]} - ${#target[*]} ))
eval "(( CMP $OP 0 ))"
fi
}
unittest()
{
eval "$@" || bail_out "unittest failed:" "$@"
}
unittest "version_matches 1.2.3 '==1.2.3'"
unittest "version_matches 1.2.3 '>=1.2.3'"
unittest "version_matches 1.2.3 '<=1.2.3'"
unittest "! version_matches 1.2.3 '!=1.2.3'"
unittest "! version_matches 1.2.3 '<1.2.3'"
unittest "! version_matches 1.2.3 '<1.2.3'"
unittest "version_matches 2 '==2.0.0'"
unittest "version_matches 2.31 '>=2.0.0&<3.0.0'"
unittest "! version_matches 2.31 '>=2.0.0&<3.0.0&!=2.31.0'"
unittest "version_matches 2.31 '>=2.0.0&<3.0.0&!=2.31.0,2.31.0'"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment