feat: tag support for skip/stop, requiring a force, similar to rm -f
This commit is contained in:
parent
f68a92323c
commit
8cf79c854b
5 changed files with 239 additions and 36 deletions
23
README.md
23
README.md
|
|
@ -5,8 +5,8 @@ Usage:
|
||||||
nag list all alarms
|
nag list all alarms
|
||||||
nag <time> <message...> one-shot alarm
|
nag <time> <message...> one-shot alarm
|
||||||
nag every <rules> <time> <message...> repeating alarm
|
nag every <rules> <time> <message...> repeating alarm
|
||||||
nag stop <id> delete alarm
|
nag stop <id|tag> delete alarm(s)
|
||||||
nag skip <id> skip next occurrence
|
nag skip <id|tag> skip next occurrence(s)
|
||||||
nag tag <id> <tags...> add tags to an alarm
|
nag tag <id> <tags...> add tags to an alarm
|
||||||
nag tag <tag> list alarms with a tag
|
nag tag <tag> list alarms with a tag
|
||||||
nag untag <id> <tags...> remove tags from an alarm
|
nag untag <id> <tags...> remove tags from an alarm
|
||||||
|
|
@ -130,10 +130,10 @@ Examples:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Usage:
|
Usage:
|
||||||
nag stop <id>
|
nag stop <id|tag>
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
Stop an alarm by ID.
|
Stop an alarm by ID, or stop all alarms with a tag (requires -f).
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `nag skip`
|
#### `nag skip`
|
||||||
|
|
@ -146,11 +146,12 @@ Description:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Usage:
|
Usage:
|
||||||
nag skip <id>
|
nag skip <id|tag>
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
Skip the next occurrence of a repeating alarm (reschedule without firing).
|
Skip the next occurrence of a repeating alarm (reschedule without firing).
|
||||||
For one-shot alarms, this deletes them.
|
For one-shot alarms, this deletes them. With a tag, applies to all
|
||||||
|
matching alarms (requires -f).
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `nag tag`
|
#### `nag tag`
|
||||||
|
|
@ -216,10 +217,11 @@ Description:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Usage:
|
Usage:
|
||||||
nag mute
|
nag mute [<tag>]
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
Mute alarm sounds. Notifications still fire, but no sound is played.
|
Mute alarm sounds. With no argument, mutes all alarms globally.
|
||||||
|
With a tag, mutes only alarms with that tag.
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `nag unmute`
|
#### `nag unmute`
|
||||||
|
|
@ -232,10 +234,11 @@ Description:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Usage:
|
Usage:
|
||||||
nag unmute
|
nag unmute [<tag>]
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
Unmute alarm sounds.
|
Unmute alarm sounds. With no argument, unmutes everything.
|
||||||
|
With a tag, unmutes only that tag.
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `nag edit`
|
#### `nag edit`
|
||||||
|
|
|
||||||
186
nag
186
nag
|
|
@ -1192,8 +1192,8 @@ Usage:
|
||||||
${_ME} list all alarms
|
${_ME} list all alarms
|
||||||
${_ME} <time> <message...> one-shot alarm
|
${_ME} <time> <message...> one-shot alarm
|
||||||
${_ME} every <rules> <time> <message...> repeating alarm
|
${_ME} every <rules> <time> <message...> repeating alarm
|
||||||
${_ME} stop <id> delete alarm
|
${_ME} stop <id|tag> delete alarm(s)
|
||||||
${_ME} skip <id> skip next occurrence
|
${_ME} skip <id|tag> skip next occurrence(s)
|
||||||
${_ME} tag <id> <tags...> add tags to an alarm
|
${_ME} tag <id> <tags...> add tags to an alarm
|
||||||
${_ME} tag <tag> list alarms with a tag
|
${_ME} tag <tag> list alarms with a tag
|
||||||
${_ME} untag <id> <tags...> remove tags from an alarm
|
${_ME} untag <id> <tags...> remove tags from an alarm
|
||||||
|
|
@ -1300,14 +1300,20 @@ list() {
|
||||||
|
|
||||||
describe "stop" <<HEREDOC
|
describe "stop" <<HEREDOC
|
||||||
Usage:
|
Usage:
|
||||||
${_ME} stop <id>
|
${_ME} stop <id|tag>
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
Stop an alarm by ID.
|
Stop an alarm by ID, or stop all alarms with a tag (requires -f).
|
||||||
HEREDOC
|
HEREDOC
|
||||||
stop() {
|
stop() {
|
||||||
local _target_id="${1:-}"
|
local _target="${1:-}"
|
||||||
[[ -n "${_target_id}" ]] || _exit_1 printf "Usage: %s stop <id>\\n" "${_ME}"
|
[[ -n "${_target}" ]] || _exit_1 printf "Usage: %s stop <id|tag>\\n" "${_ME}"
|
||||||
|
|
||||||
|
if [[ ! "${_target}" =~ ^[0-9]+$ ]]
|
||||||
|
then
|
||||||
|
_stop_by_tag "${_target}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
_acquire_lock
|
_acquire_lock
|
||||||
_read_alarms
|
_read_alarms
|
||||||
|
|
@ -1320,8 +1326,8 @@ stop() {
|
||||||
do
|
do
|
||||||
[[ -n "${_line}" ]] || continue
|
[[ -n "${_line}" ]] || continue
|
||||||
local _id
|
local _id
|
||||||
_id="$(_get_alarm_field "${_line}" 1)"
|
_id="${_line%%$'\t'*}"
|
||||||
if [[ "${_id}" == "${_target_id}" ]]
|
if [[ "${_id}" == "${_target}" ]]
|
||||||
then
|
then
|
||||||
_found=1
|
_found=1
|
||||||
else
|
else
|
||||||
|
|
@ -1329,13 +1335,13 @@ stop() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "${_found}" -eq 0 ]]
|
if (( ! _found ))
|
||||||
then
|
then
|
||||||
_release_lock
|
_release_lock
|
||||||
_exit_1 printf "No alarm with ID %s.\\n" "${_target_id}"
|
_exit_1 printf "No alarm with ID %s.\\n" "${_target}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${#_new_alarms[@]}" -eq 0 ]]
|
if (( ${#_new_alarms[@]} == 0 ))
|
||||||
then
|
then
|
||||||
_ALARMS=()
|
_ALARMS=()
|
||||||
: > "${_ALARMS_FILE}"
|
: > "${_ALARMS_FILE}"
|
||||||
|
|
@ -1345,22 +1351,85 @@ stop() {
|
||||||
fi
|
fi
|
||||||
_release_lock
|
_release_lock
|
||||||
|
|
||||||
printf "Stopped alarm %s.\\n" "${_target_id}"
|
printf "Stopped alarm %s.\\n" "${_target}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_stop_by_tag() {
|
||||||
|
local _tag="${1}"
|
||||||
|
|
||||||
|
_acquire_lock
|
||||||
|
_read_alarms
|
||||||
|
|
||||||
|
local -a _new_alarms=()
|
||||||
|
local -a _matched=()
|
||||||
|
local _line
|
||||||
|
|
||||||
|
for _line in "${_ALARMS[@]:-}"
|
||||||
|
do
|
||||||
|
[[ -n "${_line}" ]] || continue
|
||||||
|
local _tags
|
||||||
|
_tags="$(_get_alarm_field "${_line}" 2)"
|
||||||
|
if _alarm_has_tag "${_tags}" "${_tag}"
|
||||||
|
then
|
||||||
|
_matched+=("${_line}")
|
||||||
|
else
|
||||||
|
_new_alarms+=("${_line}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#_matched[@]} == 0 ))
|
||||||
|
then
|
||||||
|
_release_lock
|
||||||
|
_exit_1 printf "No alarms tagged [%s].\\n" "${_tag}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ! _YES ))
|
||||||
|
then
|
||||||
|
_release_lock
|
||||||
|
printf "Would stop %s alarm(s) tagged [%s]. Pass -f to confirm.\\n" "${#_matched[@]}" "${_tag}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ${#_new_alarms[@]} == 0 ))
|
||||||
|
then
|
||||||
|
_ALARMS=()
|
||||||
|
: > "${_ALARMS_FILE}"
|
||||||
|
else
|
||||||
|
_ALARMS=("${_new_alarms[@]}")
|
||||||
|
_write_alarms
|
||||||
|
fi
|
||||||
|
_release_lock
|
||||||
|
|
||||||
|
local _m
|
||||||
|
for _m in "${_matched[@]}"
|
||||||
|
do
|
||||||
|
local _id _message
|
||||||
|
_id="${_m%%$'\t'*}"
|
||||||
|
_message="$(_get_alarm_field "${_m}" 5)"
|
||||||
|
printf "Stopped [%s] %s\\n" "${_id}" "${_message}"
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# skip ########################################################################
|
# skip ########################################################################
|
||||||
|
|
||||||
describe "skip" <<HEREDOC
|
describe "skip" <<HEREDOC
|
||||||
Usage:
|
Usage:
|
||||||
${_ME} skip <id>
|
${_ME} skip <id|tag>
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
Skip the next occurrence of a repeating alarm (reschedule without firing).
|
Skip the next occurrence of a repeating alarm (reschedule without firing).
|
||||||
For one-shot alarms, this deletes them.
|
For one-shot alarms, this deletes them. With a tag, applies to all
|
||||||
|
matching alarms (requires -f).
|
||||||
HEREDOC
|
HEREDOC
|
||||||
skip() {
|
skip() {
|
||||||
local _target_id="${1:-}"
|
local _target="${1:-}"
|
||||||
[[ -n "${_target_id}" ]] || _exit_1 printf "Usage: %s skip <id>\\n" "${_ME}"
|
[[ -n "${_target}" ]] || _exit_1 printf "Usage: %s skip <id|tag>\\n" "${_ME}"
|
||||||
|
|
||||||
|
if [[ ! "${_target}" =~ ^[0-9]+$ ]]
|
||||||
|
then
|
||||||
|
_skip_by_tag "${_target}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
_acquire_lock
|
_acquire_lock
|
||||||
_read_alarms
|
_read_alarms
|
||||||
|
|
@ -1379,7 +1448,7 @@ skip() {
|
||||||
_rule="$(_get_alarm_field "${_line}" 4)"
|
_rule="$(_get_alarm_field "${_line}" 4)"
|
||||||
_message="$(_get_alarm_field "${_line}" 5)"
|
_message="$(_get_alarm_field "${_line}" 5)"
|
||||||
|
|
||||||
if [[ "${_id}" == "${_target_id}" ]]
|
if [[ "${_id}" == "${_target}" ]]
|
||||||
then
|
then
|
||||||
_found=1
|
_found=1
|
||||||
if [[ -n "${_rule}" ]]
|
if [[ -n "${_rule}" ]]
|
||||||
|
|
@ -1389,22 +1458,95 @@ skip() {
|
||||||
_new_alarms+=("$(printf "%s\t%s\t%s\t%s\t%s" "${_id}" "${_tags}" "${_next_ts}" "${_rule}" "${_message}")")
|
_new_alarms+=("$(printf "%s\t%s\t%s\t%s\t%s" "${_id}" "${_tags}" "${_next_ts}" "${_rule}" "${_message}")")
|
||||||
_format_time "${_next_ts}"
|
_format_time "${_next_ts}"
|
||||||
_human_time="${REPLY}"
|
_human_time="${REPLY}"
|
||||||
printf "Skipped. Next: %s\\n" "${_human_time}"
|
printf "Skipped [%s] %s — next: %s\\n" "${_id}" "${_message}" "${_human_time}"
|
||||||
else
|
else
|
||||||
printf "Stopped alarm %s.\\n" "${_id}"
|
printf "Stopped [%s] %s\\n" "${_id}" "${_message}"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
_new_alarms+=("${_line}")
|
_new_alarms+=("${_line}")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "${_found}" -eq 0 ]]
|
if (( ! _found ))
|
||||||
then
|
then
|
||||||
_release_lock
|
_release_lock
|
||||||
_exit_1 printf "No alarm with ID %s.\\n" "${_target_id}"
|
_exit_1 printf "No alarm with ID %s.\\n" "${_target}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${#_new_alarms[@]}" -eq 0 ]]
|
if (( ${#_new_alarms[@]} == 0 ))
|
||||||
|
then
|
||||||
|
_ALARMS=()
|
||||||
|
: > "${_ALARMS_FILE}"
|
||||||
|
else
|
||||||
|
_ALARMS=("${_new_alarms[@]}")
|
||||||
|
_write_alarms
|
||||||
|
fi
|
||||||
|
_release_lock
|
||||||
|
}
|
||||||
|
|
||||||
|
_skip_by_tag() {
|
||||||
|
local _tag="${1}"
|
||||||
|
|
||||||
|
_acquire_lock
|
||||||
|
_read_alarms
|
||||||
|
|
||||||
|
local -a _new_alarms=()
|
||||||
|
local _match_count=0
|
||||||
|
local _line
|
||||||
|
|
||||||
|
for _line in "${_ALARMS[@]:-}"
|
||||||
|
do
|
||||||
|
[[ -n "${_line}" ]] || continue
|
||||||
|
local _tags
|
||||||
|
_tags="$(_get_alarm_field "${_line}" 2)"
|
||||||
|
if _alarm_has_tag "${_tags}" "${_tag}"
|
||||||
|
then
|
||||||
|
_match_count=$((_match_count + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( _match_count == 0 ))
|
||||||
|
then
|
||||||
|
_release_lock
|
||||||
|
_exit_1 printf "No alarms tagged [%s].\\n" "${_tag}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ! _YES ))
|
||||||
|
then
|
||||||
|
_release_lock
|
||||||
|
printf "Would skip %s alarm(s) tagged [%s]. Pass -f to confirm.\\n" "${_match_count}" "${_tag}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for _line in "${_ALARMS[@]:-}"
|
||||||
|
do
|
||||||
|
[[ -n "${_line}" ]] || continue
|
||||||
|
local _id _tags _timestamp _rule _message
|
||||||
|
_id="$(_get_alarm_field "${_line}" 1)"
|
||||||
|
_tags="$(_get_alarm_field "${_line}" 2)"
|
||||||
|
_timestamp="$(_get_alarm_field "${_line}" 3)"
|
||||||
|
_rule="$(_get_alarm_field "${_line}" 4)"
|
||||||
|
_message="$(_get_alarm_field "${_line}" 5)"
|
||||||
|
|
||||||
|
if _alarm_has_tag "${_tags}" "${_tag}"
|
||||||
|
then
|
||||||
|
if [[ -n "${_rule}" ]]
|
||||||
|
then
|
||||||
|
local _next_ts _human_time
|
||||||
|
_next_ts="$(_next_occurrence "${_rule}" "${_timestamp}")"
|
||||||
|
_new_alarms+=("$(printf "%s\t%s\t%s\t%s\t%s" "${_id}" "${_tags}" "${_next_ts}" "${_rule}" "${_message}")")
|
||||||
|
_format_time "${_next_ts}"
|
||||||
|
_human_time="${REPLY}"
|
||||||
|
printf "Skipped [%s] %s — next: %s\\n" "${_id}" "${_message}" "${_human_time}"
|
||||||
|
else
|
||||||
|
printf "Stopped [%s] %s\\n" "${_id}" "${_message}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_new_alarms+=("${_line}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#_new_alarms[@]} == 0 ))
|
||||||
then
|
then
|
||||||
_ALARMS=()
|
_ALARMS=()
|
||||||
: > "${_ALARMS_FILE}"
|
: > "${_ALARMS_FILE}"
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ load test_helper
|
||||||
[ "${status}" -eq 0 ]
|
[ "${status}" -eq 0 ]
|
||||||
[[ "${output}" =~ "<time> <message...>" ]]
|
[[ "${output}" =~ "<time> <message...>" ]]
|
||||||
[[ "${output}" =~ "every <rules> <time> <message...>" ]]
|
[[ "${output}" =~ "every <rules> <time> <message...>" ]]
|
||||||
[[ "${output}" =~ "stop <id>" ]]
|
[[ "${output}" =~ "stop <id|tag>" ]]
|
||||||
[[ "${output}" =~ "skip <id>" ]]
|
[[ "${output}" =~ "skip <id|tag>" ]]
|
||||||
[[ "${output}" =~ "check" ]]
|
[[ "${output}" =~ "check" ]]
|
||||||
[[ "${output}" =~ "help [<subcommand>]" ]]
|
[[ "${output}" =~ "help [<subcommand>]" ]]
|
||||||
[[ "${output}" =~ "Options:" ]]
|
[[ "${output}" =~ "Options:" ]]
|
||||||
|
|
@ -39,14 +39,14 @@ load test_helper
|
||||||
run_nag help stop
|
run_nag help stop
|
||||||
[ "${status}" -eq 0 ]
|
[ "${status}" -eq 0 ]
|
||||||
[[ "${output}" =~ "Usage:" ]]
|
[[ "${output}" =~ "Usage:" ]]
|
||||||
[[ "${output}" =~ "stop <id>" ]]
|
[[ "${output}" =~ "stop <id|tag>" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "help skip shows skip usage" {
|
@test "help skip shows skip usage" {
|
||||||
run_nag help skip
|
run_nag help skip
|
||||||
[ "${status}" -eq 0 ]
|
[ "${status}" -eq 0 ]
|
||||||
[[ "${output}" =~ "Usage:" ]]
|
[[ "${output}" =~ "Usage:" ]]
|
||||||
[[ "${output}" =~ "skip <id>" ]]
|
[[ "${output}" =~ "skip <id|tag>" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "help every shows every usage" {
|
@test "help every shows every usage" {
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,32 @@ load test_helper
|
||||||
run_nag skip
|
run_nag skip
|
||||||
[ "${status}" -eq 1 ]
|
[ "${status}" -eq 1 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "skip by tag requires -f" {
|
||||||
|
run_nag every day "tomorrow 3pm" "daily work"
|
||||||
|
run_nag tag 1 work
|
||||||
|
run "${_NAG}" skip work
|
||||||
|
[ "${status}" -eq 0 ]
|
||||||
|
[[ "${output}" =~ "Would skip" ]]
|
||||||
|
[[ "${output}" =~ "-f" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "skip by tag with -f reschedules matching alarms" {
|
||||||
|
run_nag every day "tomorrow 3pm" "daily work"
|
||||||
|
run_nag tag 1 work
|
||||||
|
local _old_ts
|
||||||
|
_old_ts="$(cut -f3 "${NAG_DIR}/alarms")"
|
||||||
|
run "${_NAG}" -f skip work
|
||||||
|
[ "${status}" -eq 0 ]
|
||||||
|
[[ "${output}" =~ "Skipped" ]]
|
||||||
|
[[ "${output}" =~ "daily work" ]]
|
||||||
|
local _new_ts
|
||||||
|
_new_ts="$(cut -f3 "${NAG_DIR}/alarms")"
|
||||||
|
(( _new_ts > _old_ts ))
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "skip by tag with no matches fails" {
|
||||||
|
run_nag at "tomorrow 3pm" "test alarm"
|
||||||
|
run "${_NAG}" -f skip work
|
||||||
|
[ "${status}" -eq 1 ]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,32 @@ load test_helper
|
||||||
run_nag stop
|
run_nag stop
|
||||||
[ "${status}" -eq 1 ]
|
[ "${status}" -eq 1 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "stop by tag requires -f" {
|
||||||
|
run_nag at "tomorrow 3pm" "tagged alarm"
|
||||||
|
run_nag tag 1 work
|
||||||
|
run "${_NAG}" stop work
|
||||||
|
[ "${status}" -eq 0 ]
|
||||||
|
[[ "${output}" =~ "Would stop" ]]
|
||||||
|
[[ "${output}" =~ "-f" ]]
|
||||||
|
[ -s "${NAG_DIR}/alarms" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "stop by tag with -f removes matching alarms" {
|
||||||
|
run_nag at "tomorrow 3pm" "work alarm"
|
||||||
|
run_nag tag 1 work
|
||||||
|
run_nag at "tomorrow 4pm" "personal alarm"
|
||||||
|
run "${_NAG}" -f stop work
|
||||||
|
[ "${status}" -eq 0 ]
|
||||||
|
[[ "${output}" =~ "Stopped" ]]
|
||||||
|
[[ "${output}" =~ "work alarm" ]]
|
||||||
|
run_nag
|
||||||
|
[[ "${output}" =~ "personal alarm" ]]
|
||||||
|
[[ ! "${output}" =~ "work alarm" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "stop by tag with no matches fails" {
|
||||||
|
run_nag at "tomorrow 3pm" "test alarm"
|
||||||
|
run "${_NAG}" -f stop work
|
||||||
|
[ "${status}" -eq 1 ]
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue