Goal
Add ANSI color to the human-readable CLI surfaces of iabtcfv2:
validate --text body (status words, reasons, TC strings).
- STDERR warnings (
--enable-warnings output).
- STDERR plain-text error lines (e.g.
"validate: $err\n").
Out of scope by design: any JSON output (dump, default validate, --pretty, --errors-to-stderr JSON records) stays uncolored. JSON consumers should never see ANSI escapes.
Existing rendering (what gets colorized)
_emit_validate in bin/iabtcfv2 emits one of three text shapes today:
OK <tc> vendor <id>
FAIL <tc> vendor <id>: <reason>
FAIL <tc> vendor <id>:
- <reason 1>
- <reason 2>
ERROR <tc>: <error>
STDERR carries warnings (when --enable-warnings is set) and plain text error lines from constructor failures. JSON error records (--errors-to-stderr) are excluded from colorization.
Flag and environment contract
- New flag:
--color=auto|always|never on both dump and validate. (On dump it's effectively a no-op for STDOUT but still controls STDERR warning/error coloring.)
- Default:
auto.
- Auto-detect logic: STDOUT and STDERR tested independently via
-t. Coloring on a stream is enabled only when that stream is a TTY. Piping validate --text | less keeps STDOUT plain; warnings on STDERR can still color if STDERR is a TTY.
- Honor
NO_COLOR env var (no-color.org): when set to any value (including empty), force color off regardless of --color (except --color=always, which wins per spec — actually no, NO_COLOR per spec disables color unconditionally; --color=always SHOULD override only if user explicitly asked for it. Pick one in implementation; the no-color.org spec recommends NO_COLOR is final).
- Honor
CLICOLOR_FORCE env var: when set to a non-empty/non-zero value, force color on even when piped (matches BSD ls, grep --color behavior).
- Precedence:
--color=always/never > NO_COLOR > CLICOLOR_FORCE > TTY auto-detect. Document the precedence in POD.
Color palette (prescribed)
Use Term::ANSIColor (core since Perl 5.6, zero new dependencies).
| Element |
Color |
OK status word |
green bold |
FAIL status word |
red bold |
ERROR status word |
red bold |
TC string (the <tc> token in any line) |
dim |
Vendor id (the <id> token) |
cyan |
Reason text (after : or in bullets) |
default (no color) |
Bullet markers - |
default |
| STDERR warning lines (full line) |
yellow |
| STDERR plain-text error lines (full line) |
red |
| JSON output (any subcommand, any flag) |
never colored |
Rationale: the status word is the scannable signal; reasons are the message and must stay readable; TC strings are high-noise and benefit from being de-emphasized.
Implementation notes
- A small
_paint($color, $text) helper that returns the input unchanged when coloring is disabled keeps call sites short and testable.
- The decision "should I color stream X?" should be computed once per subcommand invocation, after option parsing, separately for STDOUT and STDERR, and stashed in
%opts.
Term::ANSIColor is loaded only when coloring is enabled, to keep --color=never callers from paying the import cost.
- Help /
--help / short-help output stays uncolored (predictable, scrape-friendly).
- The dump and validate help blocks (the
subcommand_registry rows in bin/iabtcfv2) get a new [--color=WHEN, '', 'Colorize text output (auto, always, never)'] row appended.
Tests
t/10-cli-iabtcfv2.t grows subtests:
validate --text --color=never <fixture> → output exactly equals current uncolored output (regression baseline; safe to run in CI without TTY tricks).
validate --text --color=always <valid> → output contains the green-bold OK escape sequence.
validate --text --color=always <invalid> → output contains the red-bold FAIL escape sequence.
validate --text --color=always with NO_COLOR=1 env var → output uncolored (verifies NO_COLOR precedence — or document the chosen precedence and test it).
validate --text (no flag) under a piped STDOUT → output uncolored (verifies auto-detect off-when-piped).
- A subtest forcing TTY emulation is not required —
--color=always is sufficient to exercise the colorize path; auto-detection logic is tested via the env-var precedence cases.
- All existing CLI subtests must keep passing without modification (they don't use
--color, so they get the default auto → off-when-piped behavior under prove).
Documentation
bin/iabtcfv2 POD documents --color, the precedence rules, and the palette above.
- A short note at the top of the VALIDATE/Output section explicitly states JSON paths are never colored.
xt/spelling.t may need entries for ANSI, colorize, NO_COLOR, CLICOLOR_FORCE.
Out of scope
- Coloring JSON output (any flavor).
- Coloring help /
--help / short-help output.
- Theming or custom user palettes (single fixed palette only).
- Windows-specific ANSI initialization beyond what
Term::ANSIColor already does (modern Win10+ terminals support ANSI natively; older cmd.exe falls back to plain).
Definition of done
--color=auto|always|never wired into both subcommands; default auto; NO_COLOR and CLICOLOR_FORCE honored.
- Independent TTY detection on STDOUT and STDERR.
- Palette matches the table above, applied only to text-mode output and STDERR warnings/errors.
- No JSON output path emits ANSI escapes under any flag combination.
- POD documents the flag, precedence, and palette.
t/10-cli-iabtcfv2.t covers the subtests listed; existing subtests still pass.
prove -lv t/10-cli-iabtcfv2.t passes on the v0.400 floor (Perl 5.8.9).
perlcritic, perltidy, xt/spelling.t clean.
References
Goal
Add ANSI color to the human-readable CLI surfaces of
iabtcfv2:validate --textbody (status words, reasons, TC strings).--enable-warningsoutput)."validate: $err\n").Out of scope by design: any JSON output (
dump, defaultvalidate,--pretty,--errors-to-stderrJSON records) stays uncolored. JSON consumers should never see ANSI escapes.Existing rendering (what gets colorized)
_emit_validateinbin/iabtcfv2emits one of three text shapes today:STDERR carries warnings (when
--enable-warningsis set) and plain text error lines from constructor failures. JSON error records (--errors-to-stderr) are excluded from colorization.Flag and environment contract
--color=auto|always|neveron bothdumpandvalidate. (Ondumpit's effectively a no-op for STDOUT but still controls STDERR warning/error coloring.)auto.-t. Coloring on a stream is enabled only when that stream is a TTY. Pipingvalidate --text | lesskeeps STDOUT plain; warnings on STDERR can still color if STDERR is a TTY.NO_COLORenv var (no-color.org): when set to any value (including empty), force color off regardless of--color(except--color=always, which wins per spec — actually no, NO_COLOR per spec disables color unconditionally;--color=alwaysSHOULD override only if user explicitly asked for it. Pick one in implementation; the no-color.org spec recommendsNO_COLORis final).CLICOLOR_FORCEenv var: when set to a non-empty/non-zero value, force color on even when piped (matches BSDls,grep--color behavior).--color=always/never>NO_COLOR>CLICOLOR_FORCE> TTY auto-detect. Document the precedence in POD.Color palette (prescribed)
Use
Term::ANSIColor(core since Perl 5.6, zero new dependencies).OKstatus wordgreen boldFAILstatus wordred boldERRORstatus wordred bold<tc>token in any line)dim<id>token)cyan:or in bullets)-yellowredRationale: the status word is the scannable signal; reasons are the message and must stay readable; TC strings are high-noise and benefit from being de-emphasized.
Implementation notes
_paint($color, $text)helper that returns the input unchanged when coloring is disabled keeps call sites short and testable.%opts.Term::ANSIColoris loaded only when coloring is enabled, to keep--color=nevercallers from paying the import cost.--help/ short-help output stays uncolored (predictable, scrape-friendly).subcommand_registryrows inbin/iabtcfv2) get a new[--color=WHEN, '', 'Colorize text output (auto, always, never)']row appended.Tests
t/10-cli-iabtcfv2.tgrows subtests:validate --text --color=never <fixture>→ output exactly equals current uncolored output (regression baseline; safe to run in CI without TTY tricks).validate --text --color=always <valid>→ output contains the green-boldOKescape sequence.validate --text --color=always <invalid>→ output contains the red-boldFAILescape sequence.validate --text --color=alwayswithNO_COLOR=1env var → output uncolored (verifiesNO_COLORprecedence — or document the chosen precedence and test it).validate --text(no flag) under a piped STDOUT → output uncolored (verifies auto-detect off-when-piped).--color=alwaysis sufficient to exercise the colorize path; auto-detection logic is tested via the env-var precedence cases.--color, so they get the defaultauto→ off-when-piped behavior under prove).Documentation
bin/iabtcfv2POD documents--color, the precedence rules, and the palette above.xt/spelling.tmay need entries forANSI,colorize,NO_COLOR,CLICOLOR_FORCE.Out of scope
--help/ short-help output.Term::ANSIColoralready does (modern Win10+ terminals support ANSI natively; oldercmd.exefalls back to plain).Definition of done
--color=auto|always|neverwired into both subcommands; defaultauto;NO_COLORandCLICOLOR_FORCEhonored.t/10-cli-iabtcfv2.tcovers the subtests listed; existing subtests still pass.prove -lv t/10-cli-iabtcfv2.tpasses on the v0.400 floor (Perl 5.8.9).perlcritic,perltidy,xt/spelling.tclean.References
bin/iabtcfv2,_emit_validate(search forOK,FAIL,ERRORstatus words).NO_COLORstandard: https://no-color.org/Term::ANSIColor: https://metacpan.org/pod/Term::ANSIColor (Perl 5.6+ core).--no-tc-string/--failures-only).