fluvio_extension_common/
lib.rs

1use serde::{Deserialize, Serialize};
2pub mod output;
3mod common;
4
5#[cfg(feature = "target")]
6pub mod tls;
7
8#[cfg(feature = "installation")]
9pub mod installation;
10
11pub use common::*;
12pub use crate::output::Terminal;
13use fluvio_index::{PackageId, MaybeVersion};
14
15pub const COMMAND_TEMPLATE: &str = "{about}
16
17{usage}
18
19{all-args}
20";
21
22#[macro_export]
23macro_rules! t_print {
24    ($out:expr,$($arg:tt)*) => ( $out.print(&format!($($arg)*)))
25}
26
27#[macro_export]
28macro_rules! t_println {
29    ($out:expr,$($arg:tt)*) => ( $out.println(&format!($($arg)*)))
30}
31
32#[macro_export]
33macro_rules! t_print_cli_err {
34    ($out:expr,$x:expr) => {
35        t_println!($out, "\x1B[1;31merror:\x1B[0m {}", $x);
36    };
37}
38
39/// Metadata that plugins may provide to Fluvio at runtime.
40///
41/// This allows `fluvio` to include external plugins in the help
42/// menu, version printouts, and automatic updates.
43#[derive(Debug, Serialize, Deserialize)]
44pub struct FluvioExtensionMetadata {
45    /// The title is a human-readable pretty name
46    #[serde(alias = "command")]
47    pub title: String,
48    /// Identifies the plugin on the package index
49    ///
50    /// Example: `fluvio/fluvio-cloud`
51    #[serde(default)]
52    pub package: Option<PackageId<MaybeVersion>>,
53    /// A brief description of what this plugin does
54    pub description: String,
55    /// The version of this plugin
56    pub version: semver::Version,
57}
58
59#[derive(Debug)]
60pub struct PrintTerminal {}
61
62impl PrintTerminal {
63    pub fn new() -> Self {
64        Self {}
65    }
66}
67
68impl Default for PrintTerminal {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74impl Terminal for PrintTerminal {
75    fn print(&self, msg: &str) {
76        print!("{msg}");
77    }
78
79    fn println(&self, msg: &str) {
80        println!("{msg}");
81    }
82}
83
84#[cfg(feature = "target")]
85pub mod target {
86    use std::io::Error as IoError;
87    use std::convert::TryInto;
88    use clap::Parser;
89
90    use anyhow::Result;
91
92    use fluvio::FluvioClusterConfig;
93    use fluvio::FluvioError;
94    use fluvio::Fluvio;
95    use fluvio::config::ConfigFile;
96    use crate::tls::TlsClientOpt;
97
98    #[derive(thiserror::Error, Debug)]
99    pub enum TargetError {
100        #[error(transparent)]
101        IoError(#[from] IoError),
102        #[error("Fluvio client error")]
103        ClientError(#[from] FluvioError),
104        #[error("Invalid argument: {0}")]
105        InvalidArg(String),
106        #[error("Unknown error: {0}")]
107        Other(String),
108    }
109
110    impl TargetError {
111        pub fn invalid_arg(reason: impl Into<String>) -> Self {
112            Self::InvalidArg(reason.into())
113        }
114    }
115
116    /// server configuration
117    #[derive(Debug, Parser, Default, Clone)]
118    pub struct ClusterTarget {
119        /// Address of cluster
120        #[arg(short = 'c', long, value_name = "host:port")]
121        pub cluster: Option<String>,
122
123        #[clap(flatten)]
124        pub tls: TlsClientOpt,
125
126        #[arg(short = 'P', long, value_name = "profile")]
127        pub profile: Option<String>,
128    }
129
130    impl ClusterTarget {
131        /// helper method to connect to fluvio
132        pub async fn connect(self) -> Result<Fluvio> {
133            let fluvio_config = self.load()?;
134            Fluvio::connect_with_config(&fluvio_config).await
135        }
136
137        /// try to create sc config
138        pub fn load(self) -> Result<FluvioClusterConfig> {
139            let tls = self.tls.try_into()?;
140
141            use fluvio::config::TlsPolicy::*;
142            match (self.profile, self.cluster) {
143                // Profile and Cluster together is illegal
144                (Some(_profile), Some(_cluster)) => Err(TargetError::invalid_arg(
145                    "cluster addr is not valid when profile is used",
146                )
147                .into()),
148                (Some(profile), _) => {
149                    // Specifying TLS is illegal when also giving a profile
150                    if let Anonymous | Verified(_) = tls {
151                        return Err(TargetError::invalid_arg(
152                            "tls is not valid when profile is is used",
153                        )
154                        .into());
155                    }
156
157                    let cluster = FluvioClusterConfig::load_with_profile(&profile)?
158                        .ok_or_else(|| IoError::other("Cluster not found for profile"))?;
159                    Ok(cluster.clone())
160                }
161                (None, Some(cluster)) => {
162                    let cluster = FluvioClusterConfig::new(cluster).with_tls(tls);
163                    Ok(cluster)
164                }
165                (None, None) => {
166                    // TLS specification is illegal without Cluster
167                    if let Anonymous | Verified(_) = tls {
168                        return Err(TargetError::invalid_arg(
169                            "tls is only valid if cluster addr is used",
170                        )
171                        .into());
172                    }
173
174                    // Try to use the default cluster from saved config
175                    let config_file = ConfigFile::load(None)?;
176                    let cluster = config_file.config().current_cluster()?;
177                    Ok(cluster.clone())
178                }
179            }
180        }
181    }
182}