malevich-cli 0.1.0

kaz: pipe data to an honest terminal plot — a stdin-first CLI over malevich
//! Hand-written help — a product surface, not a generated afterthought (D-C8).
//!
//! Every page carries curated, runnable pipeline examples and is snapshot-tested
//! so it cannot rot. `-h` is height, not help; help is `--help` only.

use crate::args::Command;

/// The help text for a topic: the top-level page, or one subcommand's page.
pub fn text(topic: Option<Command>) -> &'static str {
    match topic {
        None => TOP,
        Some(Command::Line) => LINE,
        Some(Command::Scatter) => SCATTER,
        Some(Command::Bar) => BAR,
        Some(Command::Hist) => HIST,
        Some(Command::Count) => COUNT,
        Some(Command::Density) => DENSITY,
        Some(Command::Box) => BOX,
        Some(Command::Ecdf) => ECDF,
        Some(Command::Violin) => VIOLIN,
        Some(Command::Hist2d) => HIST2D,
        Some(Command::Heatmap) => HEATMAP,
    }
}

const TOP: &str = "\
kaz — pipe data to an honest terminal plot

Usage:
  <data> | kaz <chart> [options]
  kaz <chart> [FILE] [options]

Charts:
  line     l   line chart, one line per series     y | xy | xyy | xyxy | yx
  scatter  s   scatter plot                        xy | xyy
  bar      b   one bar per label                   label value
  hist         histogram, automatic bins           columns of numbers
  count    c   value frequencies as bars           one column of labels
  density  d   kernel density estimate             columns of numbers
  ecdf         empirical cumulative distribution   columns of numbers
  box          box plot per column                 columns are groups
  violin       violin plot per column              columns are groups
  hist2d       2D histogram (density grid)         xy
  heatmap      shade a row-major matrix            rows of numbers

The plot goes to stderr, so stdout stays the data channel; -O echoes the input
through, letting the plot sit in the middle of a pipeline:

  cat loss.tsv | kaz line -t training
  cat data.tsv | kaz line -O | next-tool
  awk '{print $5}' access.log | kaz hist
  cut -f2 species.tsv | kaz count

Options:
  -o TARGET      plot destination: stderr (default), - for stdout, or a FILE
  -O             pass input through to stdout (mid-pipeline mode)
  -d CHAR        field separator (default: any run of whitespace)
  -H             first row is a header; its names label the series
  --fmt FMT      column mapping: y | xy | xyy | xyxy | yx
  -w N, -h N     frame width and height in cells (default: detected)
  -t TITLE       plot title
  --xlabel TEXT  x-axis title
  --ylabel TEXT  y-axis title
  --xlim A,B     fix the x range to [A, B]
  --ylim A,B     fix the y range to [A, B]
  --log-x        log-scale the x axis
  --log-y        log-scale the y axis
  --time-x       read the x column as time (unix seconds or ISO 8601)
  --bins N       histogram bin count (hist; default: automatic)
  --color WHEN   auto (default) | always | never
  --charset SET  auto (default) | ascii | half | quad | sextant | braille | octant
  --pixels WHEN  auto (default) | always | never   — sixel/kitty/iTerm2 image panel
  -q             suppress the unparsed-values tally
  --live         stream stdin, repainting a line in place (see below)
  --window N     live sliding-window length (default: the frame width)
  --fps N        live repaint throttle (default: 10)
  --rate         live: plot the per-sample delta of a monotonic counter
  --version      print version
  --help         this help; per chart: kaz <chart> --help

Note: -h is height, not help. Help is --help only.

Live mode (line only):
  --live reads stdin forever, one value per line, and repaints a sliding window
  in place — the final frame stays in your scrollback, and Ctrl-C restores the
  cursor. Feed it a live source:

    ping -i.2 host | grep -oE 'time=[0-9.]+' | tr -d 'time=' | kaz line --live
    vmstat 1 | awk 'NR>2{print $1}' | kaz line --live -t runnable
    while :; do cat /proc/loadavg | cut -d' ' -f1; sleep 1; done | kaz line --live

  IMPORTANT: if the plot seems frozen, the producer is buffering — pipes hold
  output until a block fills. Unbuffer at the source:
    stdbuf -oL producer | kaz line --live       # force line buffering
    grep --line-buffered ...                     # grep's own flag
    awk '{print; fflush()}'                      # flush every line
  --rate turns a monotonic counter (bytes, packets) into a per-interval rate.

Data is parsed as fields, not CSV — no quoting, no embedded delimiters. For real
CSV, pre-shape upstream:
  xsv select 2,5 data.csv | kaz line
  mlr --c2p cut -f x,y data.csv | kaz line -H
";

const LINE: &str = "\
kaz line — line chart, one line per series

Usage:
  <data> | kaz line [options]
  kaz line FILE [options]

Columns (--fmt, default by column count):
  y      each column is a line over its row index      (default: 1 column)
  xy     first column x, second column y
  xyy    first column x, every remaining column a line  (default: 2+ columns)
  xyxy   columns pair up: (x0,y0) (x1,y1) ...
  yx     first column y, second column x                (YouPlot compatibility)

Examples:
  cat loss.tsv | kaz line -t training
  cat data.tsv | kaz line -O | next-tool        # plot on stderr, data flows on
  paste xs ys | kaz line --fmt xy
  kaz line metrics.tsv -H                        # header names label the lines
  seq 1 100 | awk '{print $1, sqrt($1)}' | kaz line
  ping -i.2 host | grep -oE 'time=[0-9.]+' | tr -d 'time=' | kaz line --live

Live streaming is line-only; see `kaz --help` for --live, --window, --fps,
--rate, and the producer-buffering note.

Shared options: kaz --help
";

const SCATTER: &str = "\
kaz scatter — scatter plot

Usage:
  <data> | kaz scatter [options]
  kaz scatter FILE [options]

Columns (--fmt):
  xy     first column x, second column y            (default: 2 columns)
  xyy    first column x, every remaining column a series
  y      a single column against its row index

Examples:
  paste heights weights | kaz scatter -t growth
  awk '{print $3, $4}' points.tsv | kaz scatter
  kaz scatter samples.tsv -H --xlabel dose --ylabel response

Shared options: kaz --help
";

const BAR: &str = "\
kaz bar — one bar per label, rising from zero

Usage:
  <data> | kaz bar [options]
  kaz bar FILE [options]

Input: `label value` per row — the first field names the bar, the second is its
height. Rows with no value leave a gap.

Examples:
  printf 'a 3\\nb 7\\nc 5\\n' | kaz bar
  awk '{print $1, $2}' totals.tsv | kaz bar -t revenue
  kaz bar sales.tsv -H

For value frequencies (counting bare labels), use `kaz count`.

Shared options: kaz --help
";

const HIST: &str = "\
kaz hist — histogram

Usage:
  <data> | kaz hist [options]
  kaz hist FILE [options]

Input: every numeric field is pooled into one distribution. Bins are sized
automatically (Sturges / Freedman-Diaconis) with nice decimal edges, or fixed
with --bins N.

Examples:
  awk '{print $5}' access.log | kaz hist -t latency
  cut -f2 measurements.tsv | kaz hist --bins 30
  kaz hist samples.txt --xlim 0,100

Shared options: kaz --help
";

const DENSITY: &str = "\
kaz density — kernel density estimate

Usage:
  <data> | kaz density [options]
  kaz density FILE [options]

Input: every numeric field is pooled, then drawn as a smooth Gaussian-KDE
curve — a histogram without the bin-edge arbitrariness.

Examples:
  cut -f3 samples.tsv | kaz density -t weights
  awk '{print $5}' access.log | kaz density

Shared options: kaz --help
";

const ECDF: &str = "\
kaz ecdf — empirical cumulative distribution

Usage:
  <data> | kaz ecdf [options]
  kaz ecdf FILE [options]

Input: every numeric field is pooled, then drawn as a step from 0 to 1 — the
fraction of values at or below each point. Reads quantiles straight off the y
axis, no binning.

Examples:
  cut -f2 latencies.tsv | kaz ecdf -t latency
  kaz ecdf samples.txt

Shared options: kaz --help
";

const BOX: &str = "\
kaz box — a box plot per column

Usage:
  <data> | kaz box [options]
  kaz box FILE [options]

Input: each column is a group — five-number summary (median, quartiles, Tukey
whiskers) with outliers as dots. -H names the groups; otherwise they are
numbered.

Examples:
  paste control treated | kaz box -H
  awk '{print $2, $3, $4}' trials.tsv | kaz box

Shared options: kaz --help
";

const VIOLIN: &str = "\
kaz violin — a violin plot per column

Usage:
  <data> | kaz violin [options]
  kaz violin FILE [options]

Input: each column is a group, drawn as a mirrored density — the shape a box
plot summarizes. -H names the groups.

Examples:
  paste a b c | kaz violin -H
  kaz violin measurements.tsv -H -t distributions

Shared options: kaz --help
";

const HIST2D: &str = "\
kaz hist2d — 2D histogram (density grid)

Usage:
  <data> | kaz hist2d [options]
  kaz hist2d FILE [options]

Input: two columns of x, y points, binned onto a uniform grid and shaded by
count, with a colorbar. Empty cells stay blank — no data is never a little data.

Examples:
  awk '{print $1, $2}' points.tsv | kaz hist2d
  kaz hist2d samples.tsv --time-x

Shared options: kaz --help
";

const HEATMAP: &str = "\
kaz heatmap — shade a row-major matrix

Usage:
  <data> | kaz heatmap [options]
  kaz heatmap FILE [options]

Input: each row of numbers is a row of the grid (first line on top), shaded by
value with a colorbar. Missing cells stay blank.

Examples:
  kaz heatmap confusion.tsv
  awk '{print $2, $3, $4, $5}' grid.tsv | kaz heatmap

Shared options: kaz --help
";

const COUNT: &str = "\
kaz count — value frequencies as bars

Usage:
  <data> | kaz count [options]
  kaz count FILE [options]

Input: the first field of each row is a category; kaz tallies how often each
appears and draws one bar per value, most frequent first. The strongest tool for
logs — no `sort | uniq -c` needed.

Examples:
  cut -f2 species.tsv | kaz count
  awk '{print $9}' access.log | kaz count -t status-codes
  git log --format='%an' | kaz count -t commits-by-author

Shared options: kaz --help
";