Skip to main content

ctl_core/
lib.rs

1//! Shared clap chassis and presentation kernel for the `*ctl` CLIs. A
2//! library, not a command.
3//!
4//! A consumer owns typed domain requests and results. ctl-core owns parsing,
5//! help, Usage mounts, color policy, terminal layout, streams, errors, and
6//! JSON emission. One serializable result feeds pretty, colorless, and JSON
7//! output.
8//!
9//! ## Quick start
10//!
11//! ```no_run
12//! use ctl_core::prelude::*;
13//! use serde::Serialize;
14//!
15//! #[derive(Parser)]
16//! #[command(version, about = "example")]
17//! struct Cli {
18//!     #[command(flatten)]
19//!     output: OutputArgs,
20//!     #[command(subcommand)]
21//!     command: Command,
22//! }
23//!
24//! #[derive(Subcommand)]
25//! enum Command {
26//!     /// Show status.
27//!     Status,
28//! }
29//!
30//! #[derive(Serialize)]
31//! struct Status {
32//!     pending: usize,
33//! }
34//!
35//! impl Present for Status {
36//!     fn present(&self) -> Document {
37//!         Document::new().fields(Fields::new().row("pending", self.pending.to_string()))
38//!     }
39//! }
40//!
41//! fn main() -> ExitCode {
42//!     App::<Cli>::new("example")
43//!         .view(|cli| cli.output.view())
44//!         .run(|cli| match cli.command {
45//!             Command::Status => Ok(Status { pending: 0 }),
46//!         })
47//! }
48//! ```
49//!
50//! The default `app` feature covers this example.
51//!
52//! ## How output flows
53//!
54//! ```text
55//! Clap types ──→ App ──→ typed domain result ──┬──→ JSON
56//!                                              └──→ Document ──→ pretty/colorless
57//! ```
58//!
59//! - Domain handlers return data. They do not print, read terminal state, or
60//!   choose the output format.
61//! - [`Present`] maps a serializable result to a semantic [`Document`].
62//! - [`View`] serializes the result for JSON and renders its document for
63//!   pretty and colorless output.
64//! - JSON always goes to stdout and never contains ANSI. Human failures go to
65//!   stderr.
66//! - The terminal engines, Comfy Table, Anstyle, and Anstream, stay private.
67//!
68//! [`prelude`] is the one import a `*ctl` binary needs.
69//!
70//! ## Cargo features
71//!
72//! Features are additive. Unused ones do not compile `clap`, `comfy-table`,
73//! or `schemars`.
74#![doc = document_features::document_features!()]
75#![cfg_attr(docsrs, feature(doc_cfg))]
76#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
77
78#[cfg(feature = "app")]
79pub mod app;
80/// Color policy (`auto` / `always` / `never`).
81pub mod color;
82#[cfg(feature = "document")]
83pub mod document;
84/// Output representation (`pretty` / `json`).
85pub mod format;
86pub mod model;
87
88#[cfg(feature = "fixture")]
89pub mod fixture;
90#[cfg(feature = "cli")]
91pub mod flags;
92#[cfg(feature = "help")]
93pub mod help;
94#[cfg(feature = "input")]
95pub mod input;
96#[cfg(feature = "render")]
97mod layout;
98#[cfg(feature = "cli")]
99pub mod parser;
100pub mod prelude;
101#[cfg(feature = "render")]
102pub mod render;
103#[cfg(feature = "served")]
104pub mod served;
105#[cfg(feature = "render")]
106mod style;
107#[cfg(feature = "surface-model")]
108pub mod surface;
109#[cfg(feature = "render")]
110pub mod table;
111#[cfg(feature = "usage")]
112pub mod usage;
113#[cfg(feature = "validate")]
114pub mod validate;
115#[cfg(feature = "view")]
116pub mod view;
117
118#[cfg(feature = "app")]
119#[doc(hidden)]
120pub use app::App;
121#[doc(hidden)]
122pub use color::ColorMode;
123#[cfg(feature = "document")]
124pub use document::Document;
125#[cfg(feature = "document")]
126#[doc(hidden)]
127pub use document::{Block, Fields, Notice, NoticeLevel, Role, Rule, Section, Span, Table, Text};
128#[cfg(feature = "cli")]
129pub use flags::EnvPrefix;
130#[cfg(feature = "cli")]
131#[doc(hidden)]
132pub use flags::{
133    ColorLong, DryRunArgs, FlagWarning, FormatArgs, FormatLong, OutputArgs, WarningKind,
134    chassis_warnings, emit_warnings, resolve_color, switch, warn_opposites,
135};
136#[doc(hidden)]
137pub use format::OutputFormat;
138#[doc(hidden)]
139pub use indoc::{concatdoc, eprintdoc, formatdoc, indoc, printdoc, writedoc};
140#[doc(hidden)]
141pub use model::{Envelope, ErrorBody, SCHEMA_VERSION};
142#[cfg(feature = "render")]
143#[doc(hidden)]
144pub use render::{
145    DEFAULT_COLUMN_BUFFER, DEFAULT_COLUMN_BUFFER_ENV, DEFAULT_COLUMN_BUFFER_ENVS,
146    DEFAULT_FALLBACK_WIDTH, DEFAULT_LIST_STYLE_ENV, DEFAULT_LIST_STYLE_ENVS,
147    DEFAULT_MINIMUM_AUTOMATIC_WIDTH, DEFAULT_RECORD_STYLE_ENV, DEFAULT_RECORD_STYLE_ENVS,
148    DEFAULT_ROW_SEPARATION_ENV, DEFAULT_ROW_SEPARATION_ENVS, ListStyle, RecordStyle, RenderOptions,
149    Renderer, RowSeparation,
150};
151#[cfg(feature = "surface-model")]
152#[doc(hidden)]
153pub use surface::{Surface, SurfaceArgument, SurfaceCommand, SurfaceRequirement, SurfaceScope};
154#[cfg(feature = "render")]
155#[doc(hidden)]
156pub use table::{grid, kv};
157#[cfg(feature = "usage")]
158#[doc(hidden)]
159pub use usage::{mount_directives, mount_line, spec, spec_bin, take};
160#[cfg(feature = "view")]
161pub use view::Present;
162#[cfg(feature = "view")]
163#[doc(hidden)]
164pub use view::{
165    Captured, DEFAULT_JSON_LAYOUT_ENV, DEFAULT_JSON_LAYOUT_ENVS, JsonLayout, MessageKind, Stream,
166    View,
167};