Skip to content

Commit db9f89c

Browse files
authored
Merge pull request #212 from ThorstenHans/feat/formatted-output
Add support for formatting output of `cloud apps list` and `cloud apps info`
2 parents a729017 + 3a3b972 commit db9f89c

File tree

3 files changed

+113
-33
lines changed

3 files changed

+113
-33
lines changed

src/commands/apps.rs

+30-33
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use crate::commands::{client_and_app_id, create_cloud_client, CommonArgs};
1+
use crate::commands::{apps_output::AppInfo, client_and_app_id, create_cloud_client, CommonArgs};
22
use anyhow::{Context, Result};
33
use clap::Parser;
44
use cloud::{CloudClientInterface, DEFAULT_APPLIST_PAGE_SIZE};
5-
use cloud_openapi::models::{AppItem, AppItemPage, ValidationStatus};
5+
use cloud_openapi::models::{AppItem, ValidationStatus};
6+
7+
use super::apps_output::{print_app_info, print_app_list, OutputFormat};
68

79
#[derive(Parser, Debug)]
810
#[clap(about = "Manage applications deployed to Fermyon Cloud")]
@@ -19,6 +21,9 @@ pub enum AppsCommand {
1921
pub struct ListCommand {
2022
#[clap(flatten)]
2123
common: CommonArgs,
24+
/// Desired output format
25+
#[clap(value_enum, long = "format", default_value = "plain")]
26+
format: OutputFormat,
2227
}
2328

2429
#[derive(Parser, Debug)]
@@ -35,6 +40,9 @@ pub struct InfoCommand {
3540
pub app: String,
3641
#[clap(flatten)]
3742
common: CommonArgs,
43+
/// Desired output format
44+
#[clap(value_enum, long = "format", default_value = "plain")]
45+
format: OutputFormat,
3846
}
3947

4048
impl AppsCommand {
@@ -51,19 +59,21 @@ impl ListCommand {
5159
pub async fn run(self) -> Result<()> {
5260
let client = create_cloud_client(self.common.deployment_env_id.as_deref()).await?;
5361
let mut app_list_page = client.list_apps(DEFAULT_APPLIST_PAGE_SIZE, None).await?;
54-
if app_list_page.total_items <= 0 {
55-
eprintln!("No applications found");
56-
} else {
57-
print_app_list(&app_list_page);
58-
let mut page_index = 1;
59-
while !app_list_page.is_last_page {
60-
app_list_page = client
61-
.list_apps(DEFAULT_APPLIST_PAGE_SIZE, Some(page_index))
62-
.await?;
63-
print_app_list(&app_list_page);
64-
page_index += 1;
62+
let mut apps: Vec<String> = vec![];
63+
let mut page_index = 1;
64+
for app in app_list_page.items {
65+
apps.push(app.name.clone());
66+
}
67+
while !app_list_page.is_last_page {
68+
app_list_page = client
69+
.list_apps(DEFAULT_APPLIST_PAGE_SIZE, Some(page_index))
70+
.await?;
71+
for app in app_list_page.items {
72+
apps.push(app.name.clone());
6573
}
74+
page_index += 1;
6675
}
76+
print_app_list(apps, self.format);
6777
Ok(())
6878
}
6979
}
@@ -92,13 +102,14 @@ impl InfoCommand {
92102

93103
let (current_domain, in_progress_domain) = domains_current_and_in_progress(&app);
94104

95-
println!("Name: {}", &app.name);
96-
print_if_present("Description: ", app.description.as_ref());
97-
print_if_present("URL: https://", current_domain);
98-
if let Some(domain) = in_progress_domain {
99-
println!("Validation for {} is in progress", domain);
100-
};
105+
let info = AppInfo::new(
106+
app.name.clone(),
107+
app.description.clone(),
108+
current_domain.cloned(),
109+
in_progress_domain.is_none(),
110+
);
101111

112+
print_app_info(info, self.format);
102113
Ok(())
103114
}
104115
}
@@ -116,17 +127,3 @@ fn domains_current_and_in_progress(app: &AppItem) -> (Option<&String>, Option<&S
116127
None => (Some(auto_domain), None),
117128
}
118129
}
119-
120-
fn print_if_present(prefix: &str, value: Option<&String>) {
121-
if let Some(val) = value {
122-
if !val.is_empty() {
123-
println!("{prefix}{val}");
124-
}
125-
}
126-
}
127-
128-
fn print_app_list(page: &AppItemPage) {
129-
for app in &page.items {
130-
println!("{}", app.name);
131-
}
132-
}

src/commands/apps_output.rs

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use std::fmt::Display;
2+
3+
use clap::ValueEnum;
4+
use serde::Serialize;
5+
6+
#[derive(Debug, ValueEnum, PartialEq, Clone)]
7+
pub(crate) enum OutputFormat {
8+
Plain,
9+
Json,
10+
}
11+
12+
#[derive(Serialize)]
13+
pub(crate) struct AppInfo {
14+
name: String,
15+
description: String,
16+
url: Option<String>,
17+
#[serde(rename = "domainInfo")]
18+
domain_info: DomainInfo,
19+
}
20+
21+
#[derive(Serialize)]
22+
pub(crate) struct DomainInfo {
23+
domain: Option<String>,
24+
#[serde(rename = "validationFinished")]
25+
validation_finished: bool,
26+
}
27+
28+
impl AppInfo {
29+
pub(crate) fn new(
30+
name: String,
31+
description: Option<String>,
32+
domain: Option<String>,
33+
domain_validation_finished: bool,
34+
) -> Self {
35+
let url = domain.as_ref().map(|d| format!("https://{}", d));
36+
Self {
37+
name,
38+
description: description.unwrap_or_default(),
39+
url,
40+
domain_info: DomainInfo {
41+
domain,
42+
validation_finished: domain_validation_finished,
43+
},
44+
}
45+
}
46+
}
47+
48+
impl Display for AppInfo {
49+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50+
writeln!(f, "Name: {}", self.name)?;
51+
if !self.description.is_empty() {
52+
writeln!(f, "Description: {}", self.description)?;
53+
}
54+
if let Some(domain) = self.domain_info.domain.as_ref() {
55+
writeln!(f, "URL: https://{}", domain)?;
56+
if !self.domain_info.validation_finished {
57+
writeln!(f, "Validation for {} is in progress", domain)?;
58+
};
59+
}
60+
Ok(())
61+
}
62+
}
63+
64+
pub(crate) fn print_app_list(apps: Vec<String>, format: OutputFormat) {
65+
match format {
66+
OutputFormat::Json => println!("{}", serde_json::to_string_pretty(&apps).unwrap()),
67+
OutputFormat::Plain => {
68+
if apps.is_empty() {
69+
eprintln!("No applications found");
70+
return;
71+
}
72+
println!("{}", apps.join("\n"))
73+
}
74+
}
75+
}
76+
77+
pub(crate) fn print_app_info(app: AppInfo, format: OutputFormat) {
78+
match format {
79+
OutputFormat::Json => println!("{}", serde_json::to_string_pretty(&app).unwrap()),
80+
OutputFormat::Plain => print!("{}", app),
81+
}
82+
}

src/commands/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod apps;
2+
pub mod apps_output;
23
pub mod deploy;
34
pub mod key_value;
45
pub mod link;

0 commit comments

Comments
 (0)