testmark.bash
## testmark.bash: read testmark documents (data hunks embedded in markdown) from bash.
##
## Source this file, by a path containing a slash; it sources the rest of the library from beside itself.
## Needs bash 4.4 or later; on older ones, sourcing fails.
## Every public function is a shim over a private `_testmark_*` implementation, run through `_testmark_call`.
## Data comes out on stdout as exact bytes; errors go to stderr as "testmark: ...", with a nonzero status.
## Documents are addressed by a handle the caller picks at parse time; handles match `[A-Za-z0-9_]+`.
# Checked first, in syntax bash 3.2 (macOS's /bin/bash) still runs, so an old bash gets one clear error.
if ((BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4))); then
>&2 echo "testmark: needs bash 4.4 or later; this is $BASH_VERSION"
return 1
fi
# shellcheck source=lib/testmark/document.bash
source "${BASH_SOURCE[0]%/*}/testmark/document.bash"
## Runs an implementation function in a known environment, whatever the caller's settings.
##
## - `LC_ALL=C`: string operations work on bytes, and `[[:space:]]` is exactly ASCII whitespace.
## - `nocasematch` off: pattern matches are exact. It's restored afterwards, since `local -` doesn't cover shopts.
## - The `||` list: inside it, the caller's `set -e` and ERR trap can't fire on the implementation's ordinary nonzero statuses.
_testmark_call() {
local LC_ALL=C _testmark_st=0 _testmark_nocase=
case ":$BASHOPTS:" in *:nocasematch:*) _testmark_nocase=1 && shopt -u nocasematch ;; esac
"$@" || _testmark_st=$?
[[ -z $_testmark_nocase ]] || shopt -s nocasematch
return "$_testmark_st"
}
## Parses FILE and makes it available under HANDLE.
## Parsing again under the same handle replaces the earlier document; if that parse fails, the handle is left unusable.
##
## - @param $1 handle
## - @param $2 file path; also used in error messages.
## - @stderr on failure, one line: "testmark: FILE:LINE: problem" for parse errors.
testmark_parse() { _testmark_call _testmark_parse "$@"; }
## - @param $1 handle
## - @stdout the document's hunk names, one per line, in document order.
testmark_names() { _testmark_call _testmark_names "$@"; }
## - @param $1 handle
## - @param $2 hunk name
## - @stdout the hunk's body, exactly: its lines, each ending in a newline, or nothing for an empty hunk.
testmark_body() { _testmark_call _testmark_body "$@"; }
## - @param $1 handle
## - @param $2 hunk name
## - @stdout the hunk's info string (the opening fence's text after the backticks, trimmed), then a newline.
testmark_info() { _testmark_call _testmark_info "$@"; }
lib/testmark.bash · 2.6 KiB · 8618f82f0e