ctl_core/shell.rs
1//! The shells a CLI writes scripts for, and the script a shell verb
2//! prints.
3//!
4//! [`Shell`] needs the `shell` feature. [`Script`] and [`view`] also need
5//! `view`. The `completion` and `shell-init` features build the verbs that
6//! return a [`Script`].
7#![cfg_attr(
8 not(feature = "view"),
9 doc = "",
10 doc = "[`Script`]: crate#cargo-features",
11 doc = "[`view`]: crate#cargo-features"
12)]
13
14#[cfg(feature = "view")]
15use serde::Serialize;
16
17#[cfg(feature = "view")]
18use crate::color::ColorMode;
19#[cfg(feature = "view")]
20use crate::document::Document;
21#[cfg(feature = "view")]
22use crate::format::OutputFormat;
23#[cfg(feature = "view")]
24use crate::view::{Present, View};
25
26/// A shell a CLI prints a script for: the set Homebrew installs
27/// completion for by default.
28///
29/// Every verb that takes a `Shell` supports every variant. A shell joins the
30/// set only when each of those verbs has a tested form for it.
31///
32/// The set is closed, and the enum is deliberately not `#[non_exhaustive]`.
33/// A caller matches on every variant, so the compiler shows each place a
34/// new shell needs a script. Adding a shell is therefore a breaking release.
35///
36/// The command-line value is the lowercase name: `bash`, `zsh`, or `fish`.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, clap::ValueEnum)]
38#[cfg_attr(feature = "json", derive(serde::Serialize))]
39#[cfg_attr(feature = "json", serde(rename_all = "lowercase"))]
40pub enum Shell {
41 /// Bash.
42 Bash,
43 /// Zsh.
44 Zsh,
45 /// Fish.
46 Fish,
47}
48
49/// One script for one shell, the result of a shell verb such as
50/// `completion` or `init`.
51///
52/// Pretty and colourless output print the script as it is. JSON carries it
53/// under `text`, beside the shell's name under `shell`.
54#[cfg(feature = "view")]
55#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
56pub struct Script {
57 /// The shell that reads the script.
58 pub shell: Shell,
59 /// The script.
60 pub text: String,
61}
62
63#[cfg(feature = "view")]
64impl Present for Script {
65 fn present(&self) -> Document {
66 Document::new().verbatim(self.text.clone())
67 }
68}
69
70/// The view a shell verb prints its [`Script`] through: `format`, no colour,
71/// and never quiet.
72///
73/// The script is the verb's whole result. `source <(toy completion zsh)` or
74/// `eval "$(toy init zsh)"` would load nothing if `--quiet` or a quiet
75/// variable hid it. Route the verb here from [`App::view`].
76#[cfg_attr(
77 feature = "app",
78 doc = "",
79 doc = "[`App::view`]: crate::app::App::view"
80)]
81#[cfg_attr(
82 not(feature = "app"),
83 doc = "",
84 doc = "[`App::view`]: crate#cargo-features"
85)]
86#[cfg(feature = "view")]
87#[must_use]
88pub const fn view(format: OutputFormat) -> View {
89 View::new(format, ColorMode::Never)
90}