#!/usr/bin/env bash set -euo pipefail if [ -z "$HABITS" ]; then echo "Define a HABITS variable with a list of habits" fi if [ -z "$METRICS" ]; then echo "Define a METRICS variable with a list of habits" fi HABITS_FILE="${HABITS_FILE:-$HOME/.local/share/os/habits.md}" usage() { cat <<'EOF' Usage: habits Commands: open Open the habits file in $EDITOR new Add a new day to the habits file left Print today's habits that are not done yet EOF } ensure_file() { mkdir -p "$(dirname "$HABITS_FILE")" touch "$HABITS_FILE" } date_add_days() { local day="$1" offset="$2" date_arg epoch date_arg="$day $offset days" if date -d "$date_arg" +%F >/dev/null 2>&1; then date -d "$date_arg" +%F return fi # BSD/macOS date. Parse at noon to avoid midnight DST edges. epoch="$(date -j -f '%F %H:%M:%S' "$day 12:00:00" +%s)" epoch=$((epoch + offset * 86400)) date -r "$epoch" +%F } open_habits() { ensure_file "${EDITOR:-vi}" "$HABITS_FILE" } print_day() { local day="$1" printf '# %s\n\n' "$day" printf '## Habits\n\n' while IFS= read -r habit; do [ -n "$habit" ] && printf -- '- [ ] %s\n' "$habit" done <<< "$HABITS" printf '\n## Metrics \n\n' while IFS= read -r metric; do [ -n "$metric" ] && printf -- '- %s\n' "$metric:" done <<< "$METRICS" printf '\n' } new_day() { ensure_file local today latest start day tmp added today="$(date +%F)" latest="$(grep -E '^# [0-9]{4}-[0-9]{2}-[0-9]{2}$' "$HABITS_FILE" | sed 's/^# //' | sort | tail -n 1 || true)" if [ -n "$latest" ] && [[ "$latest" > "$today" ]]; then echo "Latest habits entry is in the future: $latest" >&2 exit 1 fi if [ "$latest" = "$today" ]; then echo "Habits entries already exist through $today: $HABITS_FILE" return fi if [ -n "$latest" ]; then start="$(date_add_days "$latest" 1)" else start="$today" fi tmp="$(mktemp)" added=0 { day="$today" while [[ "$day" > "$start" || "$day" = "$start" ]]; do print_day "$day" added=$((added + 1)) day="$(date_add_days "$day" -1)" done cat "$HABITS_FILE" } > "$tmp" mv "$tmp" "$HABITS_FILE" echo "Added $added habits entr$( [ "$added" -eq 1 ] && printf 'y' || printf 'ies' ) through $today to $HABITS_FILE" } remaining_habits() { ensure_file local date date="$(date +%F)" awk -v date="$date" ' $0 == "# " date { in_day = 1; next } in_day && /^# / { exit } in_day && /^## Habits[[:space:]]*$/ { in_habits = 1; next } in_day && in_habits && /^## / { exit } in_day && in_habits && /^- \[ \] / { sub(/^- \[ \] /, "") print } ' "$HABITS_FILE" } case "${1:-}" in open) open_habits ;; new) new_day open_habits ;; left) remaining_habits ;; -h|--help|help) usage ;; *) usage >&2 exit 1 ;; esac