Skip to main content

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}