output

package
v2.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultMaxBodyBytes int64 = 100 * 1024 * 1024

DefaultMaxBodyBytes is the default cap on response body reads (100 MiB). A server cannot allocate more than this per response.

Variables

View Source
var HTTPPreambleLexer = lexers.Register(chroma.MustNewLexer(
	&chroma.Config{
		Name:    "Restish HTTP Preamble",
		Aliases: []string{"restish-http"},
	},
	func() chroma.Rules {
		return chroma.Rules{

			"statusline": {
				{Pattern: `HTTP/\S+`, Type: chroma.NameNamespace},
				{Pattern: `[ \t]+`, Type: chroma.Text},
				{Pattern: `2\d\d`, Type: chroma.GenericInserted},
				{Pattern: `3\d\d`, Type: chroma.GenericOutput},
				{Pattern: `[45]\d\d`, Type: chroma.GenericError},
				{Pattern: `[^\n]+`, Type: chroma.Text},
				{Pattern: `\n`, Type: chroma.Text, Mutator: chroma.Push("headers")},
			},

			"headers": {
				{Pattern: `([\w][\w-]*)(:)`, Type: chroma.ByGroups(httpHeaderKey, chroma.Punctuation), Mutator: chroma.Push("headervalue")},
				{Pattern: `\n`, Type: chroma.Text},
			},
			"headervalue": {
				{Pattern: `[ \t]+`, Type: chroma.Text},
				{Pattern: `[^\n]+`, Type: chroma.Text},
				{Pattern: `\n`, Type: chroma.Text, Mutator: chroma.Pop(1)},
			},
			"root": {chroma.Include("statusline")},
		}
	},
))

HTTPPreambleLexer tokenizes the status line and headers section of an HTTP response so they can be colored via restishStyle just like the body.

Token mapping (all defined in style.go):

HTTP/x.x          → NameNamespace  (gray)
2xx               → GenericInserted (green)
3xx               → GenericOutput   (amber)
4xx / 5xx         → GenericError    (pink)
header name       → httpHeaderKey   (sky blue)
: separator       → Punctuation     (gray)
everything else   → Text
View Source
var ReadableLexer = lexers.Register(chroma.MustNewLexer(
	&chroma.Config{
		Name:    "Restish Readable",
		Aliases: []string{"restish-readable"},
	},
	func() chroma.Rules {
		return chroma.Rules{
			"whitespace": {
				{Pattern: `\s+`, Type: chroma.Text},
			},

			"scalar": {
				{Pattern: `(true|false|null)\b`, Type: chroma.KeywordConstant},

				{Pattern: `"?0x[0-9a-f]+(\\.\\.\\.)?"?`, Type: chroma.LiteralNumberHex},

				{Pattern: `"?[0-9]{4}-[0-9]{2}-[0-9]{2}(T[0-9:+\-.]+Z?)?"?`, Type: chroma.LiteralDate},

				{Pattern: `"?[A-Z][a-z]{2}, [0-9]{2} [A-Z][a-z]{2} [0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} GMT"?`, Type: chroma.LiteralDate},
				{Pattern: `-?(0|[1-9]\d*)(\.\d+[eE](\+|-)?\d+|[eE](\+|-)?\d+|\.\d+)`, Type: chroma.LiteralNumberFloat},
				{Pattern: `-?(0|[1-9]\d*)`, Type: chroma.LiteralNumberInteger},

				{Pattern: `"([a-z]+://|/)(\\\\|\\"|[^"])+"`, Type: chroma.LiteralStringSymbol},

				{Pattern: `"(\\\\|\\"|[^"])*"`, Type: chroma.LiteralStringDouble},
			},

			"objectrow": {
				{Pattern: `:`, Type: chroma.Punctuation},
				{Pattern: `,`, Type: chroma.Punctuation},
				{Pattern: `\n`, Type: chroma.Punctuation, Mutator: chroma.Pop(1)},
				{Pattern: `\}`, Type: chroma.Punctuation, Mutator: chroma.Pop(2)},
				chroma.Include("value"),
			},

			"object": {
				chroma.Include("whitespace"),

				{Pattern: `\}`, Type: chroma.EmitterFunc(readableIndentEnd), Mutator: chroma.Pop(1)},

				{Pattern: `(\\\\|\\:|[^:])+`, Type: chroma.NameTag, Mutator: chroma.Push("objectrow")},
			},

			"arrayvalue": {
				{Pattern: `,`, Type: chroma.Punctuation},
				{Pattern: `\]`, Type: chroma.EmitterFunc(readableIndentEnd), Mutator: chroma.Pop(1)},
				chroma.Include("value"),
			},

			"value": {
				chroma.Include("whitespace"),
				{Pattern: `\{`, Type: chroma.EmitterFunc(readableIndentStart), Mutator: chroma.Push("object")},
				{Pattern: `\[`, Type: chroma.EmitterFunc(readableIndentStart), Mutator: chroma.Push("arrayvalue")},
				chroma.Include("scalar"),
			},
			"root": {chroma.Include("value")},
		}
	},
))

ReadableLexer is a custom chroma lexer for Restish terminal output. It extends JSON tokenization with special-case patterns for:

  • ISO 8601 / HTTP dates → LiteralDate
  • URLs → LiteralStringSymbol
  • Hex binary ("0x…") → LiteralNumberHex
  • Nested bracket pairs → alternating bracket-depth token types

Ported from v1's cli/lexer.go and adapted for chroma v2 + valid JSON output (quoted keys, commas between items).

View Source
var SchemaLexer = lexers.Register(chroma.MustNewLexer(
	&chroma.Config{
		Name:    "Restish Schema",
		Aliases: []string{"schema", "restish-schema", "openapi-schema"},
	},
	func() chroma.Rules {
		return chroma.Rules{
			"whitespace": {
				{Pattern: `\s+`, Type: chroma.Text},
			},
			"scalar": {
				{Pattern: `(true|false|null)\b`, Type: chroma.KeywordConstant},
				{Pattern: `-?(0|[1-9]\d*)(\.\d+[eE](\+|-)?\d+|[eE](\+|-)?\d+|\.\d+)`, Type: chroma.LiteralNumberFloat},
				{Pattern: `-?(0|[1-9]\d*)`, Type: chroma.LiteralNumberInteger},
				{Pattern: `"([a-z]+://|/)(\\\\|\\"|[^"])+"`, Type: chroma.LiteralStringSymbol},
				{Pattern: `"(\\\\|\\"|[^"])*"`, Type: chroma.LiteralStringDouble},
				{Pattern: `<[^>\n]+>`, Type: chroma.Comment},
				{Pattern: `(allOf|oneOf|anyOf)\b`, Type: chroma.Keyword},
				{Pattern: `\(`, Type: chroma.Punctuation, Mutator: chroma.Push("annotation")},
				{Pattern: `[^{}\[\]:,\n]+`, Type: chroma.Text},
			},
			"annotation": {
				{Pattern: `\)`, Type: chroma.Punctuation, Mutator: chroma.Pop(1)},
				{Pattern: `\s+`, Type: chroma.Text},
				{Pattern: `\|`, Type: chroma.Operator},
				{Pattern: `(boolean|integer|number|string|array|object|any)\b`, Type: chroma.KeywordType},
				{Pattern: `(format|default|enum|pattern|minLen|maxLen):`, Type: chroma.NameBuiltin},
				{Pattern: `-?(0|[1-9]\d*)(\.\d+[eE](\+|-)?\d+|[eE](\+|-)?\d+|\.\d+)`, Type: chroma.LiteralNumberFloat},
				{Pattern: `-?(0|[1-9]\d*)`, Type: chroma.LiteralNumberInteger},
				{Pattern: `true|false|null\b`, Type: chroma.KeywordConstant},
				{Pattern: `[^)\s|]+`, Type: chroma.LiteralString},
			},
			"schema-key": {
				{Pattern: `\*`, Type: chroma.Operator},
				{Pattern: `[^\*:]+`, Type: chroma.NameTag},
			},
			"objectrow": {
				{Pattern: `:`, Type: chroma.Punctuation},
				{Pattern: `,`, Type: chroma.Punctuation},
				{Pattern: `\n`, Type: chroma.Punctuation, Mutator: chroma.Pop(1)},
				{Pattern: `\}`, Type: chroma.EmitterFunc(readableIndentEnd), Mutator: chroma.Pop(2)},
				chroma.Include("value"),
			},
			"object": {
				chroma.Include("whitespace"),
				{Pattern: `\}`, Type: chroma.EmitterFunc(readableIndentEnd), Mutator: chroma.Pop(1)},
				{Pattern: `([^:\n{}\[\]]+)(:)`, Type: chroma.ByGroups(chroma.UsingSelf("schema-key"), chroma.Punctuation), Mutator: chroma.Push("objectrow")},
			},
			"arrayvalue": {
				{Pattern: `,`, Type: chroma.Punctuation},
				{Pattern: `\]`, Type: chroma.EmitterFunc(readableIndentEnd), Mutator: chroma.Pop(1)},
				chroma.Include("value"),
			},
			"value": {
				chroma.Include("whitespace"),
				{Pattern: `\{`, Type: chroma.EmitterFunc(readableIndentStart), Mutator: chroma.Push("object")},
				{Pattern: `\[`, Type: chroma.EmitterFunc(readableIndentStart), Mutator: chroma.Push("arrayvalue")},
				chroma.Include("scalar"),
			},
			"root": {chroma.Include("value")},
		}
	},
))

SchemaLexer tokenizes the compact schema language used in generated OpenAPI command help. It intentionally shares theme tokens with readable output so schema blocks embedded in Glamour-rendered help are colored like response bodies.

Functions

func BuildTheme

func BuildTheme(entries ThemeEntries) (*chroma.Style, error)

BuildTheme validates user theme entries and returns a Chroma style.

func ColorEnabled

func ColorEnabled(w io.Writer) bool

ColorEnabled reports whether ANSI color output should be used for w. Rules (in priority order):

  1. NOCOLOR or NO_COLOR env var → off
  2. COLOR env var → on
  3. w is a TTY → on; otherwise off

func DefaultFormatters

func DefaultFormatters() map[string]Formatter

DefaultFormatters returns the built-in set of formatters. The "table" entry here uses default (zero-value) column settings; callers that need --rsh-columns / --rsh-sort-by should replace it.

func FormatterNames

func FormatterNames(fmts map[string]Formatter) string

FormatterNames returns the sorted list of registered formatter names, suitable for use in help text and error messages.

func Header(headers map[string][]string, name string) string

Header returns the first value for name from headers, matching http.Header.Get's case-insensitive lookup semantics.

func HeaderValues

func HeaderValues(headers map[string][]string, name string) []string

HeaderValues returns all values for name from headers.

func HighlightWithLexer

func HighlightWithLexer(lexer chroma.Lexer, data []byte) ([]byte, error)

HighlightWithLexer renders data with the provided chroma lexer and the shared Restish terminal style. It falls back to the original data if highlighting fails.

func IsLineScalar

func IsLineScalar(value any) bool

IsLineScalar reports whether value can be rendered as one unquoted line.

func IsTerminal

func IsTerminal(w io.Writer) bool

IsTerminal reports whether w is a real terminal (TTY).

func IsTerminalReader

func IsTerminalReader(r io.Reader) bool

IsTerminalReader reports whether r is a real terminal (TTY). Used to detect whether stdin is interactive.

func MarkdownStyle

func MarkdownStyle() ansi.StyleConfig

MarkdownStyle returns the Restish Glamour style, with colors derived from the active user theme so Markdown bodies and help match auto terminal output.

func NewMarkdownRenderer

func NewMarkdownRenderer(width int) (*glamour.TermRenderer, error)

NewMarkdownRenderer returns a Glamour renderer that respects GLAMOUR_STYLE when explicitly configured, otherwise falling back to the active Restish theme-backed Markdown style.

func ResponseHasNoBody added in v2.1.0

func ResponseHasNoBody(resp *http.Response) bool

ResponseHasNoBody reports whether HTTP semantics forbid a response body. Some servers still send Content-Encoding on these responses; callers should ignore the body instead of attempting to decompress or decode it.

func SetTheme

func SetTheme(entries ThemeEntries) error

SetTheme overlays user-supplied theme entries onto the built-in Restish theme. Passing nil or an empty map restores the default style.

func StatusToExitCode

func StatusToExitCode(status int) int

StatusToExitCode maps an HTTP status code to Restish's script-friendly status-family exit code.

2xx → 0  (success)
3xx → 3  (redirect status)
4xx → 4  (client error)
5xx → 5  (server error)
other → 1  (runtime or unexpected status failure)

func StyleText

func StyleText(tokenName, text string) string

StyleText renders text with the named Restish theme token. It returns the original text if the token is unknown or terminal formatting is unavailable.

func ThemeTokenColor added in v2.1.0

func ThemeTokenColor(tokenName string) string

ThemeTokenColor returns the active theme's foreground color for tokenName. It returns an empty string when the token is unknown or has no color.

func WriteLinesValue

func WriteLinesValue(w io.Writer, value any) error

WriteLinesValue writes a scalar, or an array of scalars, as unquoted lines.

Types

type AutoFormatter

type AutoFormatter struct{}

AutoFormatter writes the response body in the default human-friendly format. It does not write status or headers; callers that want interactive HTTP context should print those parts explicitly before rendering the body.

func (*AutoFormatter) Format

func (f *AutoFormatter) Format(w io.Writer, resp *Response, color bool) error

func (*AutoFormatter) StartFramedValueStream

func (f *AutoFormatter) StartFramedValueStream(w io.Writer, base *Response, color bool, frame FramedValueTemplate) (ValueStream, error)

StartFramedValueStream renders streamed values into a JSON-shaped frame

func (*AutoFormatter) StartValueStream

func (f *AutoFormatter) StartValueStream(w io.Writer, base *Response, color bool) (ValueStream, error)

StartValueStream renders each streamed value as a pretty-printed JSON block for fast human feedback on TTYs.

type CBORFormatter

type CBORFormatter struct{}

CBORFormatter encodes the response body as CBOR and writes the raw bytes to stdout. Useful for feeding binary-safe pipelines.

func (*CBORFormatter) Format

func (f *CBORFormatter) Format(w io.Writer, resp *Response, color bool) error

type Formatter

type Formatter interface {
	Format(w io.Writer, resp *Response, color bool) error
}

Formatter renders a normalized Response to a writer. color indicates whether ANSI escape sequences are appropriate.

type FramedValueStreamFormatter

type FramedValueStreamFormatter interface {
	StartFramedValueStream(w io.Writer, base *Response, color bool, frame FramedValueTemplate) (ValueStream, error)
}

FramedValueStreamFormatter can render a sequence of body/sub-values into a larger framed document shape such as a JSON array inside an object.

type FramedValueTemplate

type FramedValueTemplate struct {
	Prefix      string
	Suffix      string
	ItemIndent  string
	CloseIndent string
}

FramedValueTemplate describes how a streamed sequence of values should be embedded into a larger JSON document shape.

type GronFormatter

type GronFormatter struct{}

GronFormatter renders the body as "gron" format: each leaf value on its own line as `json.<path> = <value>;`. Useful for grep-friendly output.

func (*GronFormatter) Format

func (f *GronFormatter) Format(w io.Writer, resp *Response, color bool) error

type ImageFormatter

type ImageFormatter struct{}

ImageFormatter renders image/* responses inline on a TTY using the best available terminal graphics protocol.

Protocol is selected in order:

  1. $RSH_IMAGE_PROTOCOL env var (kitty | iterm2 | halfblock)
  2. Terminal env vars ($KITTY_WINDOW_ID / $TERM=xterm-kitty → Kitty; $TERM_PROGRAM ∈ {iTerm.app, WezTerm, Hyper} → iTerm2)
  3. Unicode half-block fallback

When color is false (non-TTY), raw bytes are written unchanged.

func (*ImageFormatter) Format

func (f *ImageFormatter) Format(w io.Writer, resp *Response, color bool) error

Format renders resp.Raw as a terminal image.

type JSONFormatter

type JSONFormatter struct{}

JSONFormatter writes only the response body as indented JSON. This is the default format in non-interactive (pipe/file) mode.

func (*JSONFormatter) Format

func (f *JSONFormatter) Format(w io.Writer, resp *Response, color bool) error

type LinesFormatter

type LinesFormatter struct{}

LinesFormatter renders scalar values as shell-friendly text, one value per line. Structured values are rejected so callers do not accidentally lose object or array shape.

func (*LinesFormatter) Format

func (f *LinesFormatter) Format(w io.Writer, resp *Response, color bool) error

func (*LinesFormatter) FormatValue

func (f *LinesFormatter) FormatValue(w io.Writer, value any, color bool) error

func (*LinesFormatter) StartValueStream

func (f *LinesFormatter) StartValueStream(w io.Writer, base *Response, color bool) (ValueStream, error)

type NDJSONFormatter

type NDJSONFormatter struct{}

NDJSONFormatter renders one JSON value per line. This is the explicit record-oriented formatter for paginated item streams and event streams.

func (*NDJSONFormatter) Format

func (f *NDJSONFormatter) Format(w io.Writer, resp *Response, color bool) error

func (*NDJSONFormatter) FormatValue

func (f *NDJSONFormatter) FormatValue(w io.Writer, value any, color bool) error

func (*NDJSONFormatter) StartValueStream

func (f *NDJSONFormatter) StartValueStream(w io.Writer, base *Response, color bool) (ValueStream, error)

type PluginFormatter

type PluginFormatter struct {
	PluginPath string
	FormatName string
	Context    context.Context
}

PluginFormatter is an output.Formatter backed by a hook plugin. The plugin receives a short formatter session over CBOR on stdin and writes its formatted output directly to stdout (raw bytes, no CBOR reply framing).

func (*PluginFormatter) Format

func (f *PluginFormatter) Format(w io.Writer, resp *Response, color bool) error

Format sends the response to the plugin using the formatter session protocol and copies the plugin's raw output to w.

func (*PluginFormatter) FormatValue

func (f *PluginFormatter) FormatValue(w io.Writer, value any, color bool) error

FormatValue renders a body/sub-value through a short formatter session, without implying that the value is a full HTTP response.

func (*PluginFormatter) StartValueStream

func (f *PluginFormatter) StartValueStream(w io.Writer, base *Response, color bool) (ValueStream, error)

StartValueStream starts a long-lived formatter plugin session.

type Response

type Response struct {
	Proto   string              `json:"proto"`
	Status  int                 `json:"status"`
	Headers map[string][]string `json:"headers"`
	// URL is the final response URL. It is used for presentation concerns such
	// as syntax highlighting file-like text responses by extension.
	URL string `json:"-"`
	// Links is populated by hypermedia parsers; empty until then.
	Links map[string]any `json:"links,omitempty"`
	Body  any            `json:"body"`
	// Raw holds the unformatted response body after Content-Encoding
	// decompression. Used by raw CLI output and binary/content-aware formatters
	// to write body bytes without formatter re-encoding.
	Raw []byte `json:"-"`
}

Response is the normalized form of every HTTP response before formatting. All formatters receive this struct; nothing downstream touches *http.Response.

func Normalize

func Normalize(resp *http.Response, reg *content.Registry, maxBytes int64) (*Response, error)

Normalize reads resp.Body, decodes it using the provided content registry, and returns a Response. resp.Body is fully consumed and closed before this returns. maxBytes caps the body read; pass DefaultMaxBodyBytes or 0 to use the default.

type TableFormatter

type TableFormatter struct {
	// Columns is the ordered list of column names to display. When empty,
	// all keys found in the first row are used.
	Columns []string
	// SortBy is an optional column name to sort rows by ascending value.
	// Numeric cells sort numerically; other cells sort by display string.
	SortBy string
}

TableFormatter renders an array of objects as a Unicode box-drawing table. Non-array or non-object body values are formatted as plain JSON.

func (*TableFormatter) Format

func (f *TableFormatter) Format(w io.Writer, resp *Response, color bool) error

type ThemeEntries

type ThemeEntries map[string]string

ThemeEntries maps token names to Chroma style descriptors. Token names may be Chroma token type names (for example, "NameTag") or Restish aliases such as "key", "header_key", "keyword", "url", and "status_2xx".

func ParseThemeJSON

func ParseThemeJSON(data []byte) (ThemeEntries, error)

ParseThemeJSON parses a direct JSON or JSONC token map and validates it by building a style.

type ValueFormatter

type ValueFormatter interface {
	FormatValue(w io.Writer, value any, color bool) error
}

ValueFormatter renders a body/sub-value without implying that it is a full HTTP response. This is used for filtered values, paginated item streams, and event streams where status/header preambles would be misleading.

type ValueStream

type ValueStream interface {
	WriteValue(value any) error
	Close() error
}

ValueStream receives a sequence of body/sub-values for a single logical output session.

type ValueStreamFormatter

type ValueStreamFormatter interface {
	StartValueStream(w io.Writer, base *Response, color bool) (ValueStream, error)
}

ValueStreamFormatter can hold formatter-specific state across a stream of body/sub-values, such as writing one CSV header followed by many rows.

type YAMLFormatter

type YAMLFormatter struct{}

YAMLFormatter serialises the response body as YAML.

func (*YAMLFormatter) Format

func (f *YAMLFormatter) Format(w io.Writer, resp *Response, color bool) error

func (*YAMLFormatter) FormatValue

func (f *YAMLFormatter) FormatValue(w io.Writer, value any, color bool) error

FormatValue writes a body/sub-value as YAML.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL