use anyhow::Result;
use clap::Subcommand;
use colored::Colorize;
use serde::Serialize;
use crate::commands::tracked::tracked_op;
use crate::output::OutputFormat;
use raps_dm::DataManagementClient;
#[derive(Debug, Subcommand)]
pub enum HubCommands {
List,
Info {
hub_id: String,
},
}
impl HubCommands {
pub async fn execute(
self,
client: &DataManagementClient,
output_format: OutputFormat,
) -> Result<()> {
match self {
HubCommands::List => list_hubs(client, output_format).await,
HubCommands::Info { hub_id } => hub_info(client, &hub_id, output_format).await,
}
}
}
#[derive(Serialize)]
struct HubListOutput {
id: String,
name: String,
hub_type: String,
extension_type: Option<String>,
region: Option<String>,
}
async fn list_hubs(client: &DataManagementClient, output_format: OutputFormat) -> Result<()> {
let hubs = tracked_op(
"Fetching hubs (requires 3-legged auth)",
output_format,
|| client.list_hubs(),
)
.await?;
let hub_outputs: Vec<HubListOutput> = hubs
.iter()
.map(|h| {
let extension_type = h
.attributes
.extension
.as_ref()
.and_then(|e| e.extension_type.as_ref())
.map(|t| extract_hub_type(t));
HubListOutput {
id: h.id.clone(),
name: h.attributes.name.clone(),
hub_type: h.hub_type.clone(),
extension_type,
region: h.attributes.region.clone(),
}
})
.collect();
if hub_outputs.is_empty() {
match output_format {
OutputFormat::Table => println!("{}", "No hubs found.".yellow()),
_ => {
output_format.write(&Vec::<HubListOutput>::new())?;
}
}
return Ok(());
}
match output_format {
OutputFormat::Table => {
println!("\n{}", "Hubs:".bold());
println!("{}", "-".repeat(80));
println!(
"{:<45} {:<15} {}",
"Hub Name".bold(),
"Type".bold(),
"Region".bold()
);
println!("{}", "-".repeat(80));
for hub in &hub_outputs {
let hub_type = hub.extension_type.as_deref().unwrap_or("Unknown");
let region = hub.region.as_deref().unwrap_or("US");
println!(
"{:<45} {:<15} {}",
hub.name.cyan(),
hub_type,
region.dimmed()
);
println!(" {} {}", "ID:".dimmed(), hub.id);
}
println!("{}", "-".repeat(80));
}
_ => {
output_format.write(&hub_outputs)?;
}
}
Ok(())
}
#[derive(Serialize)]
struct HubInfoOutput {
id: String,
name: String,
hub_type: String,
region: Option<String>,
extension_type: Option<String>,
}
async fn hub_info(
client: &DataManagementClient,
hub_id: &str,
output_format: OutputFormat,
) -> Result<()> {
let hub = tracked_op("Fetching hub details", output_format, || {
client.get_hub(hub_id)
})
.await?;
let extension_type = hub
.attributes
.extension
.as_ref()
.and_then(|e| e.extension_type.as_ref())
.map(|t| extract_hub_type(t));
let output = HubInfoOutput {
id: hub.id.clone(),
name: hub.attributes.name.clone(),
hub_type: hub.hub_type.clone(),
region: hub.attributes.region.clone(),
extension_type,
};
match output_format {
OutputFormat::Table => {
println!("\n{}", "Hub Details".bold());
println!("{}", "-".repeat(60));
println!(" {} {}", "Name:".bold(), output.name.cyan());
println!(" {} {}", "ID:".bold(), output.id);
println!(" {} {}", "Type:".bold(), output.hub_type);
if let Some(ref region) = output.region {
println!(" {} {}", "Region:".bold(), region);
}
if let Some(ref ext_type) = output.extension_type {
println!(" {} {}", "Extension:".bold(), ext_type);
}
println!("{}", "-".repeat(60));
println!(
"\n{}",
"Use 'raps project list <hub-id>' to see projects".dimmed()
);
}
_ => {
output_format.write(&output)?;
}
}
Ok(())
}
fn extract_hub_type(ext_type: &str) -> String {
if ext_type.contains("bim360") {
"BIM 360".to_string()
} else if ext_type.contains("accproject") {
"ACC".to_string()
} else if ext_type.contains("a360") {
"A360".to_string()
} else if ext_type.contains("fusion") {
"Fusion".to_string()
} else {
ext_type
.split(':')
.next_back()
.unwrap_or("Unknown")
.to_string()
}
}