Files
scwrypts/zsh/utils/io/10-print.zsh
T
wrynegade e7484103b9 v5.1.0
=====================================================================

A follow-up to V5 focused on hardening the environment system and
expanding the ZSH logging/caching toolkit. No migration required from
v5.0.x, though note the log-level reorganization below may quiet some
messages at lower verbosities.

--- New Features -------------------------

- persistent cross-runtime cache for ZSH-type scwrypts; extends the
  v5.0 runtime speedup cache to survive between runs with automatic
  hash-based invalidation (toggle via SCWRYPTS__ZSH_CACHE_ENABLED)

- new 'trace' log level (5) and echo.trace, plus '-v VARNAME' state
  injection for echo.debug / echo.trace to surface variable values
  inline

- additional scwrypts output formats: 'raw' and 'logfmt'
  (alongside pretty and json)

- normalize.boolean utility for consistent truthy/falsey handling
  across ZSH-type scwrypts

- utils.fzf.file-select for directory-scoped file selection prompts

- automatic dependency wrappers providing default arguments, GNU
  coreutil resolution, and scwrypts trace on wrapped commands

--- Changes ------------------------------

- log levels reorganized; success / status / reminder messages now
  emit at level 3, so lower verbosities will show fewer of these

- environment library restructured around get-user-json as the single
  source of truth (replaces the previous user module and cache-output)

- utility renames for consistency: user.Yn / user.yN (from utils.*)

- utils.fail and utils.abort deprecated in favor of echo.error and
  echo.error.user-abort

--- Bug Fixes ----------------------------

- CI runs now resolve config lookup-paths (.dotted.path) to their
  environment variable before reading, fixing false "not set" reports
  and invalid-export errors for lookup-path checks in CI

- hardened environment type detection so array-valued configs reprint
  correctly after their first check

- corrected exit-code capture in the cache layer (now reflects the
  cached command, not the downstream output filter)

- scwrypt exit code now survives the logging pipeline

- assorted script fixes (efs unmount file listing, postgres run-sql
  file discovery)
2026-08-14 12:28:08 -06:00

126 lines
3.2 KiB
Bash

DEPENDENCIES+=(jo jq printf)
utils.io.print() {
local \
message \
prefix prefix_delimiter=' : ' color \
minimum_log_level ignore_minimum_log_level=false \
print_to_stderr=true \
print_to_stdout=false \
last_line_end=$'\n' \
variables=() \
;
# since this function can be used outside of scwrypts, we bump log
# level to maximum to avoid confusion of "why isn't my print
# statment working"
[[ "${SCWRYPTS_LOG_LEVEL:=${LOG_LEVEL}}" ]] \
|| local SCWRYPTS_LOG_LEVEL=4
[[ "${SCWRYPTS_OUTPUT_FORMAT}" ]] \
|| local SCWRYPTS_OUTPUT_FORMAT=pretty
local _s
while [[ "${#}" -gt 0 ]]
do
_s=1
case "${1}" in
( --prefix ) _s=2; prefix="${2}" ;;
( --prefix-delimiter ) _s=2; prefix_delimiter="${2}" ;;
( --color ) _s=2; color="${2}" ;;
( --minimum-log-level ) _s=2; minimum_log_level="${2}" ;;
( --format ) _s=2; local SCWRYPTS_OUTPUT_FORMAT="${2}" ;;
( -v ) _s=2 # appends 'name=value' to the end of the message
variables+=("${2}")
;;
( --force-print )
ignore_minimum_log_level=true
;;
( --stdout )
print_to_stdout=true
print_to_stderr=false
;;
( --no-line-end ) # only applicable to some formats (see below)
last_line_end=''
;;
( * )
[[ "${message}" ]] && message+=" ${1}" || message="${1}"
;;
esac
shift "${_s}" || { echo "ERROR : missing argument for '${1}'" >&2; return 1; }
done
: \
&& [[ "${minimum_log_level}" ]] \
&& [[ "${ignore_minimum_log_level}" =~ false ]] \
&& [[ "${SCWRYPTS_LOG_LEVEL}" -lt "${minimum_log_level}" ]] \
&& return 0
local variable
for variable in "${variables[@]}"
do
message+=$'\n'" - ${variable}=${(Pq)variable}"
done
case ${SCWRYPTS_OUTPUT_FORMAT:l} in
( raw ) message+="${last_line_end}" ;;
( pretty )
message="${color}$({
while IFS='' read -r line
do
[[ ${prefix} =~ ^[[:space:]]\+$ ]] && printf '%s' $'\n'
printf "%s" "${prefix}${prefix_delimiter}$(echo "${line}" | sed 's/^ \+//; s/ \+$//')"
prefix="$(printf '%*s' ${#prefix} '')"
done <<< "${message}"
})${last_line_end}$(utils.colors.reset)"
;;
( json )
message="$(jo \
timestamp=$(date +%s) \
runtime=${SCWRYPTS_RUNTIME_ID} \
status="$(echo "${prefix}" | sed 's/ .*//')" \
message="$(echo -n "${message}" | sed 's/^\t\+//')" \
2>/dev/null || echo "{\"error\":\"your message was too long so I encoded it\",\"messageB64\":\"$(echo "${message}" | base64 | tr -d '\n')\"}"
)"$'\n'
;;
( logfmt )
local ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
local level
case "${prefix}" in
( ERROR* ) level=error ;;
( WARNING* ) level=warn ;;
( DEBUG* ) level=debug ;;
( TRACE* ) level=trace ;;
( * ) level=info ;;
esac
local msg="${message}"
msg="${msg//\\/\\\\}"
msg="${msg//\"/\\\"}"
msg="${msg//$'\n'/\\n}"
message="ts=${ts} level=${level} runtime=${SCWRYPTS_RUNTIME_ID} msg=\"${msg}\""$'\n'
;;
( * )
echo "ERROR : unsupported format '${SCWRYPTS_OUTPUT_FORMAT}'" >&2
return 1
;;
esac
[[ "${print_to_stderr}" =~ true ]] && printf "%s" "${message}" >&2
[[ "${print_to_stdout}" =~ true ]] && printf "%s" "${message}"
return 0
}