Skip to content

Commit d15a052

Browse files
authored
Merge pull request #107 from mbland/format-module
Update `lib/format` functions
2 parents d25e5d3 + 5d2158f commit d15a052

File tree

8 files changed

+230
-90
lines changed

8 files changed

+230
-90
lines changed

lib/bats/helpers

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ test_printf() {
210210
printf "$@" >&2
211211
fi
212212
}
213+
export -f test_printf
213214

214215
# Skips a test if `TEST_FILTER` is set but doesn't match `BATS_TEST_DESCRIPTION`
215216
#

lib/format

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,77 +3,113 @@
33
# Text formatting utilities
44
#
55
# Exports:
6+
# @go.array_printf
7+
# Assigns `printf` transformations of its arguments to an array
68
#
79
# @go.pad_items
8-
# Pads each string in an array to match the length of the longest element
10+
# Right-pads each string with spaces to match the length of the longest
911
#
1012
# @go.zip_items
1113
# Concatenates parallel elements from each input array
1214
#
1315
# @go.strip_formatting_codes
1416
# Strips ANSI escape codes of the form `\e[...(;...)m` from a string
1517

16-
# Pads each string in an array to match the length of the longest element
18+
. "$_GO_USE_MODULES" 'strings' 'validation'
19+
20+
# Assigns `printf` transformations of its arguments to an array
21+
#
22+
# Since `printf -v` can't print to an array subscript prior to Bash 4.1, this
23+
# provides a portable means of printing to an array variable while avoiding the
24+
# use of `eval`.
25+
#
26+
# NOTE: By default, this function relies on the ASCII Record Separator character
27+
# ($'\x1f') to delimit generated strings before splitting them into the result
28+
# array. If you have strings containing this character, you can set a new
29+
# delimiter via `_GO_ARRAY_PRINTF_DELIMITER`.
30+
#
31+
# Globals:
32+
# _GO_ARRAY_PRINTF_DELIMITER:
33+
# If set, used to separate generated strings prior to array assignment
34+
#
35+
# Arguments:
36+
# result: Name of the caller-declared output array
37+
# format: `printf`-style format specification
38+
# ...: Items to pass to `printf` and store in `result`
39+
@go.array_printf() {
40+
@go.validate_identifier_or_die 'Result array name' "$1"
41+
local __go_array_printf_delim="${_GO_ARRAY_PRINTF_DELIMITER:-$'\x1f'}"
42+
local __tmp_go_array_printf
43+
printf -v __tmp_go_array_printf -- "${2}${__go_array_printf_delim}" "${@:3}"
44+
@go.split "$__go_array_printf_delim" "$__tmp_go_array_printf" "$1"
45+
}
46+
47+
# Right-pads each string with spaces to match the length of the longest
48+
#
49+
# Globals:
50+
# _GO_ARRAY_PRINTF_DELIMITER: See the comments for `@go.array_printf`
1751
#
1852
# Arguments:
19-
# $1: Name of the input array in the caller's scope
20-
# Outputs:
21-
# __go_padded_result: The caller-declared array to which results are assigned
53+
# result: Name of the caller-declared output array
54+
# ...: Items to right-pad with spaces to match the longest one
2255
@go.pad_items() {
23-
local items_reference=("${1}[@]")
24-
local item
25-
local padding=''
26-
local padding_len=0
56+
@go.validate_identifier_or_die 'Result array name' "$1"
57+
local __go_pad_items_items=("${@:2}")
58+
local __item
59+
local padding_size=0
2760

28-
for item in "${!items_reference}"; do
29-
while [[ "${#padding}" -lt "${#item}" ]]; do
30-
padding+=' '
61+
for __item in "${__go_pad_items_items[@]}"; do
62+
while [[ "$padding_size" -lt "${#__item}" ]]; do
63+
padding_size="${#__item}"
3164
done
3265
done
33-
34-
for item in "${!items_reference}"; do
35-
padding_len="$((${#padding} - ${#item}))"
36-
__go_padded_result+=("${item}${padding:0:padding_len}")
37-
done
66+
@go.array_printf "$1" "%-${padding_size}s" "${__go_pad_items_items[@]}"
3867
}
3968

4069
# Concatenates parallel elements from each input array
4170
#
4271
# Will produce a number of results matching that of the left-hand input array.
4372
#
73+
# Globals:
74+
# _GO_ARRAY_PRINTF_DELIMITER: See the comments for `@go.array_printf`
75+
#
4476
# Arguments:
45-
# $1: Name of the left-hand input array in the caller's scope
46-
# $2: Name of the right-hand input array in the caller's scope
47-
# $3: The string used as a delimiter between elements (defaults to two spaces)
48-
# Outputs:
49-
# __go_zipped_result: The caller-declared array to which results are assigned
77+
# lhs: Name of the left-hand input array in the caller's scope
78+
# rhs: Name of the right-hand input array in the caller's scope
79+
# delim: String used as a delimiter between elements (default: two spaces)
80+
# result: Name of the caller-declared output array
5081
@go.zip_items() {
82+
@go.validate_identifier_or_die 'Result array name' "$4"
5183
local lhs_array_reference="${1}[@]"
52-
local rhs_reference="$2"
53-
local delimiter="${3:- }"
5484
local rhs_item_ref
5585
local item
56-
local i=-1
86+
local i=0
87+
local __tmp_go_zip_items_result=()
5788

5889
for item in "${!lhs_array_reference}"; do
59-
rhs_item_ref="${rhs_reference}[$((++i))]"
60-
__go_zipped_result+=("${item}${delimiter}${!rhs_item_ref}")
90+
rhs_item_ref="${2}[$((i++))]"
91+
__tmp_go_zip_items_result+=("${item}${3}${!rhs_item_ref}")
6192
done
93+
@go.array_printf "$4" '%s' "${__tmp_go_zip_items_result[@]}"
6294
}
6395

64-
# Strips ANSI escape codes of the form `\e[...(;...)m` from a string
96+
# Strips ANSI escape codes from a string
6597
#
6698
# Used primarily by `@go.log`.
6799
#
68100
# Arguments:
69-
# $1: The string to strip
70-
# Outputs:
71-
# __go_stripped_value: The caller-declared variable for the stripped result
101+
# original: The string to strip
102+
# result: Name of the caller-declared output variable
72103
@go.strip_formatting_codes() {
73-
__go_stripped_value="$1"
74-
local format_pattern='\\e\[[0-9]{1,3}(;[0-9]{1,3})*m'
104+
@go.validate_identifier_or_die 'Result variable name' "$2"
105+
printf -v "$2" -- '%b' "$1"
106+
107+
if [[ -z "$__GO_STRIP_FORMATTING_PATTERN" ]]; then
108+
printf -v __GO_STRIP_FORMATTING_PATTERN '%b' '\e[[0-9]{1,3}(;[0-9]{1,3})*m'
109+
readonly __GO_STRIP_FORMATTING_PATTERN
110+
fi
75111

76-
while [[ "$__go_stripped_value" =~ $format_pattern ]]; do
77-
__go_stripped_value="${__go_stripped_value/"${BASH_REMATCH[0]}"}"
112+
while [[ "${!2}" =~ $__GO_STRIP_FORMATTING_PATTERN ]]; do
113+
printf -v "$2" -- '%s' "${!2/"${BASH_REMATCH[0]}"}"
78114
done
79115
}

lib/log

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$'
225225
local log_level="${args[0]^^}"
226226
local exit_status=0
227227
local log_msg
228-
local __go_stripped_value
228+
local stripped_log_msg
229229
local level_fd
230230

231231
unset 'args[0]'
@@ -266,10 +266,10 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$'
266266
_@go.log_command_should_skip_file_descriptor "$level_fd"; then
267267
continue
268268
elif [[ ! -t "$level_fd" && -z "$_GO_LOG_FORMATTING" ]]; then
269-
if [[ -z "$__go_stripped_value" ]]; then
270-
@go.strip_formatting_codes "$log_msg"
269+
if [[ -z "$stripped_log_msg" ]]; then
270+
@go.strip_formatting_codes "$log_msg" 'stripped_log_msg'
271271
fi
272-
printf '%s\n' "$__go_stripped_value" >&"$level_fd"
272+
printf '%s\n' "$stripped_log_msg" >&"$level_fd"
273273
else
274274
printf '%b\n' "$log_msg" >&"$level_fd"
275275
fi

libexec/modules

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,31 +151,31 @@ _@go.modules_produce_listing() {
151151

152152
. "$_GO_CORE_DIR/lib/format"
153153

154-
local __go_padded_result=()
155-
local __go_zipped_result=()
154+
local padded_modules=()
155+
local zipped_modules=()
156156

157-
@go.pad_items 'modules'
158-
modules=("${__go_padded_result[@]}")
157+
@go.pad_items padded_modules "${modules[@]}"
158+
modules=("${padded_modules[@]}")
159159

160160
case "$action" in
161161
paths)
162162
local relative_paths=("${__go_modules[@]#$_GO_ROOTDIR/}")
163-
@go.zip_items 'modules' 'relative_paths'
163+
@go.zip_items modules relative_paths ' ' zipped_modules
164164
;;
165165
summaries)
166166
local __go_modules_summaries=()
167167
if ! _@go.modules_summaries; then
168168
return 1
169169
fi
170-
@go.zip_items 'modules' '__go_modules_summaries'
170+
@go.zip_items modules __go_modules_summaries ' ' zipped_modules
171171
;;
172172
*)
173173
# Should only happen if _@go.modules is updated and this case statement
174174
# isn't.
175175
@go.printf 'ERROR: Unknown action: %s\n' "$action" >&2
176176
return 1
177177
esac
178-
__go_modules_listing=("${__go_zipped_result[@]}")
178+
__go_modules_listing=("${zipped_modules[@]}")
179179
}
180180

181181
_@go.modules_search() {

tests/bats-helpers.bats

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ teardown() {
145145

146146
@test "$SUITE: test_printf" {
147147
create_bats_test_script test-script \
148-
". '$_GO_CORE_DIR/lib/bats/helpers'" \
149148
"test_printf '%s\n' 'some test debug output'"
150149

151150
run "$BATS_TEST_ROOTDIR/test-script"

0 commit comments

Comments
 (0)