Skip to main content

openstack_cli_core/
cli.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//! CLI top level command and processing
15//!
16use clap::builder::{
17    Styles,
18    styling::{AnsiColor, Effects},
19};
20use clap::{Args, Parser, ValueEnum, ValueHint};
21use clap_complete::Shell;
22
23use crate::error::OpenStackCliError;
24
25use crate::config::{Config, ConfigError};
26
27pub fn styles() -> Styles {
28    Styles::styled()
29        .header(AnsiColor::Green.on_default() | Effects::BOLD)
30        .usage(AnsiColor::Green.on_default() | Effects::BOLD)
31        .literal(AnsiColor::White.on_default() | Effects::BOLD)
32        .placeholder(AnsiColor::Cyan.on_default())
33}
34
35/// Trait for getting Command execution context.
36pub trait CliArgs {
37    fn global_opts(&self) -> &GlobalOpts;
38    fn config(&self) -> &Config;
39}
40
41/// Parse config file
42pub fn parse_config(s: &str) -> Result<Config, OpenStackCliError> {
43    let mut builder = Config::builder()?;
44    if !s.is_empty() {
45        builder = builder.add_source(s).map_err(ConfigError::builder)?;
46    }
47    Ok(builder.build()?)
48}
49
50/// Connection options.
51#[derive(Args)]
52#[command(next_display_order = 800, next_help_heading = "Connection options")]
53pub struct ConnectionOpts {
54    /// Name reference to the clouds.yaml entry for the cloud configuration.
55    #[arg(long, env = "OS_CLOUD", global = true, display_order = 801, conflicts_with_all(["cloud_config_from_env", "os_cloud_name"]))]
56    pub os_cloud: Option<String>,
57
58    /// Get the cloud config from environment variables.
59    ///
60    /// Conflicts with the `--os-cloud` option. No merging of environment variables with the
61    /// options from the `clouds.yaml` file done. It is possible to rely on the `--auth-helper-cmd`
62    /// command, but than the `--os-cloud-name` should be specified to give a reasonable connection
63    /// name.
64    #[arg(long, global = true, action = clap::ArgAction::SetTrue, display_order = 802)]
65    pub cloud_config_from_env: bool,
66
67    /// Cloud name used when configuration is retrieved from environment variables. When not
68    /// specified the `envvars` would be used as a default. This value will be used eventually by
69    /// the authentication helper when data need to be provided dynamically.
70    #[arg(long, env = "OS_CLOUD_NAME", global = true, display_order = 802)]
71    pub os_cloud_name: Option<String>,
72
73    /// Project ID to use instead of the one in connection profile.
74    #[arg(long, env = "OS_PROJECT_ID", global = true, display_order = 803)]
75    pub os_project_id: Option<String>,
76
77    /// Project Name to use instead of the one in the connection profile.
78    #[arg(long, env = "OS_PROJECT_NAME", global = true, display_order = 803)]
79    pub os_project_name: Option<String>,
80
81    /// Region Name to use instead of the one in the connection profile.
82    #[arg(long, env = "OS_REGION_NAME", global = true, display_order = 804)]
83    pub os_region_name: Option<String>,
84
85    /// Custom path to the `clouds.yaml` config file.
86    #[arg(
87        long,
88        env = "OS_CLIENT_CONFIG_FILE",
89        global = true,
90        value_hint = ValueHint::FilePath,
91        display_order = 805
92    )]
93    pub os_client_config_file: Option<String>,
94
95    /// Custom path to the `secure.yaml` config file.
96    #[arg(
97        long,
98        env = "OS_CLIENT_SECURE_FILE",
99        global = true,
100        value_hint = ValueHint::FilePath,
101        display_order = 805
102    )]
103    pub os_client_secure_file: Option<String>,
104
105    /// External authentication helper command.
106    ///
107    /// Invoke external command to obtain necessary connection parameters. This is a path to the
108    /// executable, which is called with first parameter being the attribute key (i.e. `password`)
109    /// and a second parameter a cloud name (whatever is used in `--os-cloud` or
110    /// `--os-cloud-name`).
111    #[arg(long, global = true, value_hint = ValueHint::ExecutablePath, display_order = 810)]
112    pub auth_helper_cmd: Option<String>,
113}
114
115/// Output configuration.
116#[derive(Args)]
117#[command(next_display_order = 900, next_help_heading = "Output options")]
118pub struct OutputOpts {
119    /// Output format.
120    #[arg(short, long, global = true, value_enum, display_order = 910)]
121    pub output: Option<OutputFormat>,
122
123    /// Fields to return in the output (only in normal and wide mode).
124    #[arg(short, long, global=true, action=clap::ArgAction::Append, display_order = 910)]
125    pub fields: Vec<String>,
126
127    /// Pretty print the output.
128    #[arg(short, long, global=true, action = clap::ArgAction::SetTrue, display_order = 910)]
129    pub pretty: bool,
130
131    /// Verbosity level. Repeat to increase level.
132    #[arg(short, long, global=true, action = clap::ArgAction::Count, display_order = 920)]
133    pub verbose: u8,
134
135    /// Output Table arrangement.
136    #[arg(long, global=true, default_value_t = TableArrangement::Dynamic, value_enum, display_order = 930)]
137    pub table_arrangement: TableArrangement,
138
139    /// Record HTTP request timings.
140    #[arg(long, global=true, action = clap::ArgAction::SetTrue, display_order = 950)]
141    pub timing: bool,
142}
143
144/// Global CLI options.
145#[derive(Args)]
146#[command(next_display_order = 900)]
147pub struct GlobalOpts {
148    /// Connection options.
149    #[command(flatten)]
150    pub connection: ConnectionOpts,
151
152    /// Output config.
153    #[command(flatten)]
154    pub output: OutputOpts,
155}
156
157/// Output format.
158#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
159pub enum OutputFormat {
160    /// Json output.
161    Json,
162    /// Wide (Human readable table with extra attributes). Note: this has
163    /// effect only in list operations.
164    Wide,
165}
166
167/// Table arrangement.
168#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
169pub enum TableArrangement {
170    /// Dynamically determine the width of columns in regard to terminal width and content length.
171    /// With this mode, the content in cells will wrap dynamically to get the the best column
172    /// layout for the given content.
173    #[default]
174    Dynamic,
175    /// This is mode is the same as the `Dynamic` arrangement, but it will always use as much space
176    /// as it’s given. Any surplus space will be distributed between all columns.
177    DynamicFullWidth,
178    /// Don’t do any content arrangement. Tables with this mode might become wider than your output
179    /// and look ugly.
180    Disabled,
181}
182
183/// Output shell completion code for the specified shell (bash, zsh, fish, or powershell). The
184/// shell code must be evaluated to provide interactive completion of `osc` commands.  This can
185/// be done by sourcing it from the .bash_profile.
186///
187/// Examples:
188///
189///  Enable completion at a shell start:
190///
191/// `echo 'source <(osc completion bash)' >>~/.bashrc`
192///
193#[derive(Parser, Debug)]
194pub struct CompletionCommand {
195    /// If provided, outputs the completion file for given shell.
196    #[arg(default_value_t = Shell::Bash)]
197    pub shell: Shell,
198}