Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Added support for negative alt condition #522

Closed
wants to merge 9 commits into from
71 changes: 71 additions & 0 deletions test/test_unit_score_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,74 @@ def test_underscores_and_upper_case_in_distro_and_family(runner, yadm):
assert run.success
assert run.err == ""
assert run.out == expected

def test_negative_class_condition(runner, yadm):
"""Test negative class condition: returns 0 when matching and proper score when not matching."""
script = f"""
YADM_TEST=1 source {yadm}
local_class="testclass"
local_classes=("testclass")

# 0
score=0
score_file "filename##~class.testclass" "dest"
echo "score: $score"

# 16
score=0
score_file "filename##~class.badclass" "dest"
echo "score2: $score"

# 16
score=0
score_file "filename##~c.badclass" "dest"
echo "score3: $score"
"""
run = runner(command=["bash"], inp=script)
assert run.success
output = run.out.strip().splitlines()
assert output[0] == "score: 0"
assert output[1] == "score2: 16"
assert output[2] == "score3: 16"

def test_negative_combined_conditions(runner, yadm):
"""Test negative conditions for multiple alt types: returns 0 when matching and proper score when not matching."""
script = f"""
YADM_TEST=1 source {yadm}
local_class="testclass"
local_classes=("testclass")
local_distro="testdistro"

# (0) + (0) = 0
score=0
score_file "filename##~class.testclass,~distro.testdistro" "dest"
echo "score: $score"

# (1000 + 16) + (1000 + 4) = 2020
score=0
score_file "filename##class.testclass,distro.testdistro" "dest"
echo "score2: $score"

# 0 (negated class condition)
score=0
score_file "filename##~class.badclass,~distro.testdistro" "dest"
echo "score3: $score"

# (1000 + 16) + (4) = 1020
score=0
score_file "filename##class.testclass,~distro.baddistro" "dest"
echo "score4: $score"

# (1000 + 16) + (16) = 1032
score=0
score_file "filename##class.testclass,~class.badclass" "dest"
echo "score5: $score"
"""
run = runner(command=["bash"], inp=script)
assert run.success
output = run.out.strip().splitlines()
assert output[0] == "score: 0"
assert output[1] == "score2: 2020"
assert output[2] == "score3: 0"
assert output[3] == "score4: 1020"
assert output[4] == "score5: 1032"
27 changes: 18 additions & 9 deletions yadm
Original file line number Diff line number Diff line change
Expand Up @@ -179,32 +179,39 @@ function score_file() {
local value=${field#*.}
[ "$field" = "$label" ] && value="" # when .value is omitted

# Check for negative condition prefix (e.g., "~<label>")
local negate=0
if [ "${label:0:1}" = "~" ]; then
negate=1
label="${label:1}"
fi

shopt -s nocasematch
local -i delta=-1
local -i delta=$(( negate ? 1 : -1 ))
case "$label" in
default)
delta=0
;;
a | arch)
[[ "$value" = "$local_arch" ]] && delta=1
[[ "$value" = "$local_arch" ]] && delta=1 || delta=-1
;;
o | os)
[[ "$value" = "$local_system" ]] && delta=2
[[ "$value" = "$local_system" ]] && delta=2 || delta=-2
;;
d | distro)
[[ "${value// /_}" = "${local_distro// /_}" ]] && delta=4
[[ "${value// /_}" = "${local_distro// /_}" ]] && delta=4 || delta=-4
;;
f | distro_family)
[[ "${value// /_}" = "${local_distro_family// /_}" ]] && delta=8
[[ "${value// /_}" = "${local_distro_family// /_}" ]] && delta=8 || delta=-8
;;
c | class)
in_list "$value" "${local_classes[@]}" && delta=16
in_list "$value" "${local_classes[@]}" && delta=16 || delta=-16
;;
h | hostname)
[[ "$value" = "$local_host" ]] && delta=32
[[ "$value" = "$local_host" ]] && delta=32 || delta=-32
;;
u | user)
[[ "$value" = "$local_user" ]] && delta=64
[[ "$value" = "$local_user" ]] && delta=64 || delta=-64
;;
e | extension)
# extension isn't a condition and doesn't affect the score
Expand All @@ -230,11 +237,13 @@ function score_file() {
esac
shopt -u nocasematch

(( negate )) && delta=$((-delta))
if ((delta < 0)); then
score=0
return
fi
score=$((score + 1000 + delta))
(( negate )) || delta=$((delta + 1000))
score=$((score + delta))
done

record_score "$score" "$target" "$source" "$template_processor"
Expand Down
10 changes: 9 additions & 1 deletion yadm.1
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ These are the supported attributes, in the order of the weighted precedence:
Valid when the value matches a supported template processor.
See the TEMPLATES section for more details.
.TP
.BR user ,\ u
.BR user ,\ u\
Valid if the value matches the current user.
Current user is calculated by running
.BR "id \-u \-n" .
Expand Down Expand Up @@ -575,6 +575,7 @@ files are managed by yadm's repository:
- $HOME/path/example.txt##os.Linux
- $HOME/path/example.txt##os.Linux,hostname.host1
- $HOME/path/example.txt##os.Linux,hostname.host2
- $HOME/path/example.txt##~os.Linux,~os.Darwin,~os.SunOS

If running on a Macbook named "host2",
yadm will create a symbolic link which looks like this:
Expand All @@ -601,6 +602,13 @@ If running on a system, with class set to "Work", the link will be:

.IR $HOME/path/example.txt " -> " $HOME/path/example.txt##class.Work

Negative conditions are supported via the "~" prefix. If running within
Windows Subsystem for Linux, where the os is reported as WSL, the link will be:

.IR $HOME/path/example.txt " -> " $HOME/path/example.txt##~os.Linux,~os.Darwin,~os.SunOS

Negative conditions use the same weight which corresponds to the attached attribute.

If no "##default" version exists and no files have valid conditions, then no
link will be created.

Expand Down
Loading