use std::fmt;
use rust_i18n::t;
use tabled::Tabled;
use timeweb_rs::{
apis::{configuration::Configuration, images_api},
models
};
use crate::{error::TwcError, output::OutputFormat};
fn fmt_id<T: std::fmt::Display>(v: T) -> String {
v.to_string()
}
#[derive(Tabled)]
struct ImageRow {
#[tabled(rename = "ID")]
id: String,
#[tabled(rename = "Name")]
name: String,
#[tabled(rename = "Status")]
status: String,
#[tabled(rename = "Size")]
size: String,
#[tabled(rename = "Location")]
location: String
}
impl fmt::Display for ImageRow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {} {} {}",
self.id, self.name, self.status, self.size, self.location
)
}
}
pub async fn list(config: &Configuration, format: OutputFormat) -> Result<(), TwcError> {
let resp = images_api::get_images(config, None, None).await?;
let rows: Vec<ImageRow> = resp
.images
.iter()
.map(|i| ImageRow {
id: i.id.clone(),
name: i.name.clone(),
status: format!("{:?}", i.status),
size: format!("{} MB", i.size),
location: i.location.clone()
})
.collect();
match format {
OutputFormat::Table => {
if rows.is_empty() {
println!("{}", t!("cli.no_images"));
} else {
let table = crate::output::render_table(&rows);
println!("{table}");
}
}
OutputFormat::Json | OutputFormat::Yaml => {
let out = crate::output::serialized(format, &resp.images)
.transpose()?
.unwrap_or_default();
println!("{out}");
}
OutputFormat::Quiet => {
for i in &resp.images {
println!("{}\t{}", i.id, i.name);
}
}
}
Ok(())
}
pub async fn delete(config: &Configuration, id: &str) -> Result<(), TwcError> {
images_api::delete_image(config, id).await?;
println!("{}", t!("cli.image_deleted", id => id));
Ok(())
}
pub async fn info(config: &Configuration, id: &str, format: OutputFormat) -> Result<(), TwcError> {
let resp = images_api::get_image(config, id).await?;
let image = &resp.image;
match format {
OutputFormat::Table => {
println!("ID: {}", image.id);
println!("Name: {}", image.name);
println!("Status: {:?}", image.status);
println!("Size: {} MB", image.size);
println!("Location: {}", image.location);
println!("Disk ID: {}", fmt_id(image.disk_id));
println!("Created At: {}", image.created_at);
}
OutputFormat::Json | OutputFormat::Yaml => {
if let Some(out) = crate::output::serialized(format, &resp.image) {
println!("{}", out?);
}
}
OutputFormat::Quiet => {
println!("{}\t{}\t{:?}", image.id, image.name, image.status);
}
}
Ok(())
}
pub async fn create(
config: &Configuration,
name: &str,
location: &str,
format: OutputFormat
) -> Result<(), TwcError> {
let mut req = models::ImageInApi::new(location.to_string(), models::Os::CustomOs);
req.name = Some(name.to_string());
let resp = images_api::create_image(config, req).await?;
let image = &resp.image;
match format {
OutputFormat::Table => {
println!(
"{}",
t!("cli.image_created", name => image.name, id => image.id)
);
}
OutputFormat::Json | OutputFormat::Yaml => {
if let Some(out) = crate::output::serialized(format, &resp.image) {
println!("{}", out?);
}
}
OutputFormat::Quiet => {
println!("{}\t{}", image.id, image.name);
}
}
Ok(())
}
pub async fn set(config: &Configuration, id: &str, name: Option<&str>) -> Result<(), TwcError> {
let mut req = models::ImageUpdateApi::new();
req.name = name.map(String::from);
let resp = images_api::update_image(config, id, req).await?;
let image = &resp.image;
println!(
"{}",
t!("cli.image_updated", name => image.name, id => image.id)
);
Ok(())
}
pub async fn upload(config: &Configuration, id: &str, file: &str) -> Result<(), TwcError> {
let path = std::path::PathBuf::from(file);
let file_name = path
.file_name()
.and_then(|n| n.to_str())
.ok_or_else(|| TwcError::Api(t!("cli.image_invalid_file", path => file).into_owned()))?;
let disposition = format!("attachment; filename=\"{file_name}\"");
images_api::upload_image(config, id, path.clone(), Some(&disposition)).await?;
println!("{}", t!("cli.image_uploaded", file => file_name, id => id));
Ok(())
}