updates
This commit is contained in:
		@@ -1,9 +1,9 @@
 | 
			
		||||
// Get all servers and print them in a table
 | 
			
		||||
let servers = hetzner.get_servers();
 | 
			
		||||
print(servers);
 | 
			
		||||
print_servers_table(servers);
 | 
			
		||||
servers.pretty_print();
 | 
			
		||||
 | 
			
		||||
// Get a specific server and print its details
 | 
			
		||||
// Replace 1825193 with the server number you want to fetch
 | 
			
		||||
let server = hetzner.get_server(1825193);
 | 
			
		||||
print_server_details(server);
 | 
			
		||||
print(server);
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
// Get all SSH keys and print them in a table
 | 
			
		||||
let keys = hetzner.get_ssh_keys();
 | 
			
		||||
print_ssh_keys_table(keys);
 | 
			
		||||
print(keys);
 | 
			
		||||
 | 
			
		||||
// Get a specific SSH key
 | 
			
		||||
// Replace "13:dc:a2:1e:a9:d2:1d:a9:39:f4:44:c5:f1:00:ec:c7" with the fingerprint of the key you want to fetch
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,6 @@ pub enum AppError {
 | 
			
		||||
    RequestError(#[from] reqwest::Error),
 | 
			
		||||
    #[error("API error: {0:?}")]
 | 
			
		||||
    ApiError(ApiError),
 | 
			
		||||
    #[error("Deserialization failed: {0}\nResponse body: {1}")]
 | 
			
		||||
    DeserializationError(String, String),
 | 
			
		||||
    #[error("Deserialization Error: {0:?}")]
 | 
			
		||||
    SerdeJsonError(#[from] serde_json::Error),
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
pub mod error;
 | 
			
		||||
pub mod models;
 | 
			
		||||
 | 
			
		||||
use std::any::type_name;
 | 
			
		||||
 | 
			
		||||
use self::models::{
 | 
			
		||||
    Boot, BootWrapper, ErrorResponse, Rescue, RescueWrapper, Server, ServerWrapper, SshKey,
 | 
			
		||||
    SshKeyWrapper,
 | 
			
		||||
    Boot, Rescue, Server, SshKey,
 | 
			
		||||
};
 | 
			
		||||
use crate::config::Config;
 | 
			
		||||
use error::AppError;
 | 
			
		||||
@@ -28,23 +29,19 @@ impl Client {
 | 
			
		||||
    where
 | 
			
		||||
        T: serde::de::DeserializeOwned,
 | 
			
		||||
    {
 | 
			
		||||
        match response.status() {
 | 
			
		||||
            StatusCode::OK => {
 | 
			
		||||
                // Read the body as text first, then try to deserialize from that
 | 
			
		||||
                let text = response.text().unwrap_or_else(|_| "<failed to read body>".to_string());
 | 
			
		||||
                let result = serde_json::from_str(&text);
 | 
			
		||||
                match result {
 | 
			
		||||
                    Ok(val) => Ok(val),
 | 
			
		||||
                    Err(e) => {
 | 
			
		||||
                        let deser_err = format!("{:?}", e);
 | 
			
		||||
                        Err(AppError::DeserializationError(deser_err, text))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            _status => {
 | 
			
		||||
                let error_response: ErrorResponse = response.json()?;
 | 
			
		||||
                Err(AppError::ApiError(error_response.error))
 | 
			
		||||
            }
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let body = response.text()?;
 | 
			
		||||
 | 
			
		||||
        println!("RESPONSE: \n{}", &body);
 | 
			
		||||
        println!("Type of T to handle_response: {:#?}", type_name::<T>());
 | 
			
		||||
 | 
			
		||||
        if status == StatusCode::OK {
 | 
			
		||||
            serde_json::from_str::<T>(&body).map_err(Into::into)
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(AppError::ApiError(models::ApiError {
 | 
			
		||||
                status: status.as_u16(),
 | 
			
		||||
                message: body,
 | 
			
		||||
            }))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -58,8 +55,7 @@ impl Client {
 | 
			
		||||
            .basic_auth(&self.config.username, Some(&self.config.password))
 | 
			
		||||
            .send()?;
 | 
			
		||||
 | 
			
		||||
        let server_wrapper: ServerWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(server_wrapper.server)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_servers(&self) -> Result<Vec<Server>, AppError> {
 | 
			
		||||
@@ -69,9 +65,7 @@ impl Client {
 | 
			
		||||
            .basic_auth(&self.config.username, Some(&self.config.password))
 | 
			
		||||
            .send()?;
 | 
			
		||||
 | 
			
		||||
        let server_wrappers: Vec<ServerWrapper> = self.handle_response(response)?;
 | 
			
		||||
        let servers = server_wrappers.into_iter().map(|sw| sw.server).collect();
 | 
			
		||||
        Ok(servers)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_ssh_keys(&self) -> Result<Vec<SshKey>, AppError> {
 | 
			
		||||
        let response = self
 | 
			
		||||
@@ -80,9 +74,7 @@ impl Client {
 | 
			
		||||
            .basic_auth(&self.config.username, Some(&self.config.password))
 | 
			
		||||
            .send()?;
 | 
			
		||||
            
 | 
			
		||||
        let ssh_key_wrappers: Vec<SshKeyWrapper> = self.handle_response(response)?;
 | 
			
		||||
        let ssh_keys = ssh_key_wrappers.into_iter().map(|sw| sw.key).collect();
 | 
			
		||||
        Ok(ssh_keys)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_ssh_key(&self, fingerprint: &str) -> Result<SshKey, AppError> {
 | 
			
		||||
@@ -92,8 +84,7 @@ impl Client {
 | 
			
		||||
            .basic_auth(&self.config.username, Some(&self.config.password))
 | 
			
		||||
            .send()?;
 | 
			
		||||
        
 | 
			
		||||
        let ssh_key_wrapper: SshKeyWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(ssh_key_wrapper.key)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_ssh_key(&self, name: &str, data: &str) -> Result<SshKey, AppError> {
 | 
			
		||||
@@ -105,8 +96,7 @@ impl Client {
 | 
			
		||||
            .form(¶ms)
 | 
			
		||||
            .send()?;
 | 
			
		||||
            
 | 
			
		||||
        let ssh_key_wrapper: SshKeyWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(ssh_key_wrapper.key)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn update_ssh_key_name(
 | 
			
		||||
@@ -122,8 +112,7 @@ impl Client {
 | 
			
		||||
            .form(¶ms)
 | 
			
		||||
            .send()?;
 | 
			
		||||
            
 | 
			
		||||
        let ssh_key_wrapper: SshKeyWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(ssh_key_wrapper.key)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn delete_ssh_key(&self, fingerprint: &str) -> Result<(), AppError> {
 | 
			
		||||
@@ -141,8 +130,7 @@ impl Client {
 | 
			
		||||
            .basic_auth(&self.config.username, Some(&self.config.password))
 | 
			
		||||
            .send()?;
 | 
			
		||||
            
 | 
			
		||||
        let boot_wrapper: BootWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(boot_wrapper.boot)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_rescue_boot_configuration(
 | 
			
		||||
@@ -158,8 +146,7 @@ impl Client {
 | 
			
		||||
            .basic_auth(&self.config.username, Some(&self.config.password))
 | 
			
		||||
            .send()?;
 | 
			
		||||
 | 
			
		||||
        let rescue_wrapper: RescueWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(rescue_wrapper.rescue)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn enable_rescue_mode(
 | 
			
		||||
@@ -184,8 +171,7 @@ impl Client {
 | 
			
		||||
            .form(¶ms)
 | 
			
		||||
            .send()?;
 | 
			
		||||
            
 | 
			
		||||
        let rescue_wrapper: RescueWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(rescue_wrapper.rescue)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn disable_rescue_mode(&self, server_number: i32) -> Result<Rescue, AppError> {
 | 
			
		||||
@@ -198,7 +184,6 @@ impl Client {
 | 
			
		||||
            .basic_auth(&self.config.username, Some(&self.config.password))
 | 
			
		||||
            .send()?;
 | 
			
		||||
            
 | 
			
		||||
        let rescue_wrapper: RescueWrapper = self.handle_response(response)?;
 | 
			
		||||
        Ok(rescue_wrapper.rescue)
 | 
			
		||||
        self.handle_response(response)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Deserializer};
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use prettytable::{row, Table};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Clone)]
 | 
			
		||||
pub struct ServerWrapper {
 | 
			
		||||
@@ -46,7 +48,43 @@ impl Server {
 | 
			
		||||
            .with_get("traffic", |s: &mut Server| s.traffic.clone())
 | 
			
		||||
            .with_get("status", |s: &mut Server| s.status.clone())
 | 
			
		||||
            .with_get("cancelled", |s: &mut Server| s.cancelled)
 | 
			
		||||
            .with_get("paid_until", |s: &mut Server| s.paid_until.clone());
 | 
			
		||||
            .with_get("paid_until", |s: &mut Server| s.paid_until.clone())
 | 
			
		||||
            // when doing `print(server) in Rhai script, this will execute`
 | 
			
		||||
            .on_print(|s: &mut Server| s.to_string())
 | 
			
		||||
            // also add the pretty_print function for convience
 | 
			
		||||
            .with_fn("pretty_print", |s: &mut Server| s.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Server should always be printed as a table, hence implement the Display trait to render the table
 | 
			
		||||
impl fmt::Display for Server {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let mut table = Table::new();
 | 
			
		||||
        table.add_row(row!["Property", "Value"]);
 | 
			
		||||
        table.add_row(row!["Server Number", self.server_number.to_string()]);
 | 
			
		||||
        table.add_row(row!["Server Name", self.server_name.clone()]);
 | 
			
		||||
        table.add_row(row!["Server IP", self.server_ip.clone()]);
 | 
			
		||||
        table.add_row(row!["IPv6 Network", self.server_ipv6_net.clone()]);
 | 
			
		||||
        table.add_row(row!["Product", self.product.clone()]);
 | 
			
		||||
        table.add_row(row!["Datacenter", self.dc.clone()]);
 | 
			
		||||
        table.add_row(row!["Traffic", self.traffic.clone()]);
 | 
			
		||||
        table.add_row(row!["Status", self.status.clone()]);
 | 
			
		||||
        table.add_row(row!["Cancelled", self.cancelled.to_string()]);
 | 
			
		||||
        table.add_row(row!["Paid Until", self.paid_until.clone()]);
 | 
			
		||||
        table.add_row(row!["Reset", self.reset.unwrap_or(false).to_string()]);
 | 
			
		||||
        table.add_row(row!["Rescue", self.rescue.unwrap_or(false).to_string()]);
 | 
			
		||||
        table.add_row(row!["VNC", self.vnc.unwrap_or(false).to_string()]);
 | 
			
		||||
        table.add_row(row!["Windows", self.windows.is_some().to_string()]);
 | 
			
		||||
        table.add_row(row!["Plesk", self.plesk.is_some().to_string()]);
 | 
			
		||||
        table.add_row(row!["cPanel", self.cpanel.is_some().to_string()]);
 | 
			
		||||
        table.add_row(row!["WOL", self.wol.unwrap_or(false).to_string()]);
 | 
			
		||||
        table.add_row(row!["Hot Swap", self.hot_swap.unwrap_or(false).to_string()]);
 | 
			
		||||
        table.add_row(row![
 | 
			
		||||
            "Linked Storagebox",
 | 
			
		||||
            self.linked_storagebox
 | 
			
		||||
                .map_or("N/A".to_string(), |id| id.to_string())
 | 
			
		||||
        ]);
 | 
			
		||||
        write!(f, "{}", table)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -63,12 +101,16 @@ impl Subnet {
 | 
			
		||||
        builder
 | 
			
		||||
            .with_name("Subnet")
 | 
			
		||||
            .with_get("ip", |s: &mut Subnet| s.ip.clone())
 | 
			
		||||
            .with_get("mask", |s: &mut Subnet| s.mask.clone());
 | 
			
		||||
            .with_get("mask", |s: &mut Subnet| s.mask.clone())
 | 
			
		||||
            .on_print(|s: &mut Subnet| s.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |s: &mut Subnet| s.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#[derive(Debug, Deserialize, Clone)]
 | 
			
		||||
pub struct SshKeyWrapper {
 | 
			
		||||
    pub key: SshKey,
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Subnet {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "IP: {}, Mask: {}", self.ip, self.mask)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Clone, CustomType)]
 | 
			
		||||
@@ -92,7 +134,30 @@ impl SshKey {
 | 
			
		||||
            .with_get("key_type", |s: &mut SshKey| s.key_type.clone())
 | 
			
		||||
            .with_get("size", |s: &mut SshKey| s.size)
 | 
			
		||||
            .with_get("data", |s: &mut SshKey| s.data.clone())
 | 
			
		||||
            .with_get("created_at", |s: &mut SshKey| s.created_at.clone());
 | 
			
		||||
            .with_get("created_at", |s: &mut SshKey| s.created_at.clone())
 | 
			
		||||
            .on_print(|s: &mut SshKey| s.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |s: &mut SshKey| s.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for SshKey {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let mut table = Table::new();
 | 
			
		||||
        table.add_row(row![
 | 
			
		||||
            "Name",
 | 
			
		||||
            "Fingerprint",
 | 
			
		||||
            "Type",
 | 
			
		||||
            "Size",
 | 
			
		||||
            "Created At"
 | 
			
		||||
        ]);
 | 
			
		||||
        table.add_row(row![
 | 
			
		||||
            self.name.clone(),
 | 
			
		||||
            self.fingerprint.clone(),
 | 
			
		||||
            self.key_type.clone(),
 | 
			
		||||
            self.size.to_string(),
 | 
			
		||||
            self.created_at.clone()
 | 
			
		||||
        ]);
 | 
			
		||||
        write!(f, "{}", table)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#[derive(Debug, Deserialize, Clone)]
 | 
			
		||||
@@ -120,13 +185,30 @@ impl Boot {
 | 
			
		||||
            .with_get("vnc", |b: &mut Boot| b.vnc.clone())
 | 
			
		||||
            .with_get("windows", |b: &mut Boot| b.windows.clone())
 | 
			
		||||
            .with_get("plesk", |b: &mut Boot| b.plesk.clone())
 | 
			
		||||
            .with_get("cpanel", |b: &mut Boot| b.cpanel.clone());
 | 
			
		||||
            .with_get("cpanel", |b: &mut Boot| b.cpanel.clone())
 | 
			
		||||
            .on_print(|b: &mut Boot| b.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |b: &mut Boot| b.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Clone)]
 | 
			
		||||
pub struct RescueWrapper {
 | 
			
		||||
    pub rescue: Rescue,
 | 
			
		||||
impl fmt::Display for Boot {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let mut table = Table::new();
 | 
			
		||||
        table.add_row(row!["Configuration", "Details"]);
 | 
			
		||||
        table.add_row(row!["Rescue", self.rescue.to_string()]);
 | 
			
		||||
        table.add_row(row!["Linux", self.linux.to_string()]);
 | 
			
		||||
        table.add_row(row!["VNC", self.vnc.to_string()]);
 | 
			
		||||
        if let Some(windows) = &self.windows {
 | 
			
		||||
            table.add_row(row!["Windows", windows.to_string()]);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(plesk) = &self.plesk {
 | 
			
		||||
            table.add_row(row!["Plesk", plesk.to_string()]);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(cpanel) = &self.cpanel {
 | 
			
		||||
            table.add_row(row!["cPanel", cpanel.to_string()]);
 | 
			
		||||
        }
 | 
			
		||||
        write!(f, "{}", table)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Clone, CustomType)]
 | 
			
		||||
@@ -158,7 +240,21 @@ impl Rescue {
 | 
			
		||||
            .with_get("authorized_key", |r: &mut Rescue| {
 | 
			
		||||
                r.authorized_key.clone()
 | 
			
		||||
            })
 | 
			
		||||
            .with_get("host_key", |r: &mut Rescue| r.host_key.clone());
 | 
			
		||||
            .with_get("host_key", |r: &mut Rescue| r.host_key.clone())
 | 
			
		||||
            .on_print(|r: &mut Rescue| r.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |r: &mut Rescue| r.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Rescue {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "OS: {}, Active: {}, Keys: {}",
 | 
			
		||||
            self.os.join(", "),
 | 
			
		||||
            self.active,
 | 
			
		||||
            self.authorized_key.join(", ")
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -194,7 +290,21 @@ impl Linux {
 | 
			
		||||
            .with_get("authorized_key", |l: &mut Linux| {
 | 
			
		||||
                l.authorized_key.clone()
 | 
			
		||||
            })
 | 
			
		||||
            .with_get("host_key", |l: &mut Linux| l.host_key.clone());
 | 
			
		||||
            .with_get("host_key", |l: &mut Linux| l.host_key.clone())
 | 
			
		||||
            .on_print(|l: &mut Linux| l.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |l: &mut Linux| l.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Linux {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "Dist: {}, Lang: {}, Active: {}",
 | 
			
		||||
            self.dist.join(", "),
 | 
			
		||||
            self.lang.join(", "),
 | 
			
		||||
            self.active
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -222,7 +332,21 @@ impl Vnc {
 | 
			
		||||
            .with_get("dist", |v: &mut Vnc| v.dist.clone())
 | 
			
		||||
            .with_get("lang", |v: &mut Vnc| v.lang.clone())
 | 
			
		||||
            .with_get("active", |v: &mut Vnc| v.active)
 | 
			
		||||
            .with_get("password", |v: &mut Vnc| v.password.clone());
 | 
			
		||||
            .with_get("password", |v: &mut Vnc| v.password.clone())
 | 
			
		||||
            .on_print(|v: &mut Vnc| v.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |v: &mut Vnc| v.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Vnc {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "Dist: {}, Lang: {}, Active: {}",
 | 
			
		||||
            self.dist.join(", "),
 | 
			
		||||
            self.lang.join(", "),
 | 
			
		||||
            self.active
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -252,7 +376,21 @@ impl Windows {
 | 
			
		||||
            .with_get("dist", |w: &mut Windows| w.dist.clone())
 | 
			
		||||
            .with_get("lang", |w: &mut Windows| w.lang.clone())
 | 
			
		||||
            .with_get("active", |w: &mut Windows| w.active)
 | 
			
		||||
            .with_get("password", |w: &mut Windows| w.password.clone());
 | 
			
		||||
            .with_get("password", |w: &mut Windows| w.password.clone())
 | 
			
		||||
            .on_print(|w: &mut Windows| w.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |w: &mut Windows| w.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Windows {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "Dist: {}, Lang: {}, Active: {}",
 | 
			
		||||
            self.dist.as_deref().unwrap_or_default().join(", "),
 | 
			
		||||
            self.lang.as_deref().unwrap_or_default().join(", "),
 | 
			
		||||
            self.active
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -284,7 +422,22 @@ impl Plesk {
 | 
			
		||||
            .with_get("lang", |p: &mut Plesk| p.lang.clone())
 | 
			
		||||
            .with_get("active", |p: &mut Plesk| p.active)
 | 
			
		||||
            .with_get("password", |p: &mut Plesk| p.password.clone())
 | 
			
		||||
            .with_get("hostname", |p: &mut Plesk| p.hostname.clone());
 | 
			
		||||
            .with_get("hostname", |p: &mut Plesk| p.hostname.clone())
 | 
			
		||||
            .on_print(|p: &mut Plesk| p.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |p: &mut Plesk| p.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Plesk {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "Dist: {}, Lang: {}, Active: {}, Hostname: {}",
 | 
			
		||||
            self.dist.join(", "),
 | 
			
		||||
            self.lang.join(", "),
 | 
			
		||||
            self.active,
 | 
			
		||||
            self.hostname.as_deref().unwrap_or("N/A")
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -316,7 +469,22 @@ impl Cpanel {
 | 
			
		||||
            .with_get("lang", |c: &mut Cpanel| c.lang.clone())
 | 
			
		||||
            .with_get("active", |c: &mut Cpanel| c.active)
 | 
			
		||||
            .with_get("password", |c: &mut Cpanel| c.password.clone())
 | 
			
		||||
            .with_get("hostname", |c: &mut Cpanel| c.hostname.clone());
 | 
			
		||||
            .with_get("hostname", |c: &mut Cpanel| c.hostname.clone())
 | 
			
		||||
            .on_print(|c: &mut Cpanel| c.to_string())
 | 
			
		||||
            .with_fn("pretty_print", |c: &mut Cpanel| c.to_string());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Cpanel {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "Dist: {}, Lang: {}, Active: {}, Hostname: {}",
 | 
			
		||||
            self.dist.join(", "),
 | 
			
		||||
            self.lang.join(", "),
 | 
			
		||||
            self.active,
 | 
			
		||||
            self.hostname.as_deref().unwrap_or("N/A")
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -369,14 +537,16 @@ where
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
pub struct ApiError {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub status: i32,
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub code: String,
 | 
			
		||||
    pub status: u16,
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub message: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
pub struct ErrorResponse {
 | 
			
		||||
    pub error: ApiError,
 | 
			
		||||
impl From<reqwest::blocking::Response> for ApiError {
 | 
			
		||||
    fn from(value: reqwest::blocking::Response) -> Self {
 | 
			
		||||
        ApiError {
 | 
			
		||||
            status: value.status().into(),
 | 
			
		||||
            message: value.text().unwrap_or("The API call returned an error.".to_string()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
use crate::api::Client;
 | 
			
		||||
use crate::api::models::{Server, SshKey};
 | 
			
		||||
use crate::api::models::{Rescue, Linux, Vnc, Windows, Plesk, Cpanel, Boot, Server, SshKey};
 | 
			
		||||
use rhai::{Engine, Scope};
 | 
			
		||||
 | 
			
		||||
pub mod server;
 | 
			
		||||
pub mod ssh_keys;
 | 
			
		||||
pub mod boot;
 | 
			
		||||
pub mod printing;
 | 
			
		||||
 | 
			
		||||
pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) {
 | 
			
		||||
    let mut engine = Engine::new();
 | 
			
		||||
@@ -12,17 +13,18 @@ pub fn setup_engine(client: Client) -> (Engine, Scope<'static>) {
 | 
			
		||||
 | 
			
		||||
    engine.build_type::<Server>();
 | 
			
		||||
    engine.build_type::<SshKey>();
 | 
			
		||||
    engine.build_type::<crate::api::models::Boot>();
 | 
			
		||||
    engine.build_type::<crate::api::models::Rescue>();
 | 
			
		||||
    engine.build_type::<crate::api::models::Linux>();
 | 
			
		||||
    engine.build_type::<crate::api::models::Vnc>();
 | 
			
		||||
    engine.build_type::<crate::api::models::Windows>();
 | 
			
		||||
    engine.build_type::<crate::api::models::Plesk>();
 | 
			
		||||
    engine.build_type::<crate::api::models::Cpanel>();
 | 
			
		||||
    engine.build_type::<Boot>();
 | 
			
		||||
    engine.build_type::<Rescue>();
 | 
			
		||||
    engine.build_type::<Linux>();
 | 
			
		||||
    engine.build_type::<Vnc>();
 | 
			
		||||
    engine.build_type::<Windows>();
 | 
			
		||||
    engine.build_type::<Plesk>();
 | 
			
		||||
    engine.build_type::<Cpanel>();
 | 
			
		||||
 | 
			
		||||
    server::register(&mut engine);
 | 
			
		||||
    ssh_keys::register(&mut engine);
 | 
			
		||||
    boot::register(&mut engine);
 | 
			
		||||
    printing::register(&mut engine);
 | 
			
		||||
 | 
			
		||||
    scope.push("hetzner", client);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/scripting/printing/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/scripting/printing/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
use rhai::{Array, Engine};
 | 
			
		||||
use crate::scripting::{Server, SshKey};
 | 
			
		||||
 | 
			
		||||
mod servers_table;
 | 
			
		||||
mod ssh_keys_table;
 | 
			
		||||
 | 
			
		||||
pub fn pretty_print_dispatch(array: Array) {
 | 
			
		||||
    if array.is_empty() {
 | 
			
		||||
        println!("<empty table>");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let first = &array[0];
 | 
			
		||||
 | 
			
		||||
    if first.is::<Server>() {
 | 
			
		||||
        servers_table::pretty_print_servers(array);
 | 
			
		||||
    } else if first.is::<SshKey>() {
 | 
			
		||||
        ssh_keys_table::pretty_print_ssh_keys(array);
 | 
			
		||||
    } else {
 | 
			
		||||
        // Generic fallback for other types
 | 
			
		||||
        for item in array {
 | 
			
		||||
            println!("{}", item.to_string());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn register(engine: &mut Engine) {
 | 
			
		||||
    engine.register_fn("pretty_print", pretty_print_dispatch);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								src/scripting/printing/servers_table.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/scripting/printing/servers_table.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
use prettytable::{row, Table};
 | 
			
		||||
use rhai::Array;
 | 
			
		||||
 | 
			
		||||
use super::Server;
 | 
			
		||||
 | 
			
		||||
pub fn pretty_print_servers(servers: Array) {
 | 
			
		||||
    let mut table = Table::new();
 | 
			
		||||
    table.add_row(row![b =>
 | 
			
		||||
        "Number",
 | 
			
		||||
        "Name",
 | 
			
		||||
        "IP",
 | 
			
		||||
        "Product",
 | 
			
		||||
        "DC",
 | 
			
		||||
        "Status"
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    for server_dyn in servers {
 | 
			
		||||
        if let Some(server) = server_dyn.try_cast::<Server>() {
 | 
			
		||||
            table.add_row(row![
 | 
			
		||||
                server.server_number.to_string(),
 | 
			
		||||
                server.server_name,
 | 
			
		||||
                server.server_ip,
 | 
			
		||||
                server.product,
 | 
			
		||||
                server.dc,
 | 
			
		||||
                server.status
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    table.printstd();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/scripting/printing/ssh_keys_table.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/scripting/printing/ssh_keys_table.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
use prettytable::{row, Table};
 | 
			
		||||
use super::SshKey;
 | 
			
		||||
 | 
			
		||||
pub fn pretty_print_ssh_keys(keys: rhai::Array) {
 | 
			
		||||
    let mut table = Table::new();
 | 
			
		||||
    table.add_row(row![b =>
 | 
			
		||||
        "Name",
 | 
			
		||||
        "Fingerprint",
 | 
			
		||||
        "Type",
 | 
			
		||||
        "Size",
 | 
			
		||||
        "Created At"
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    for key_dyn in keys {
 | 
			
		||||
        if let Some(key) = key_dyn.try_cast::<SshKey>() {
 | 
			
		||||
            table.add_row(row![
 | 
			
		||||
                key.name,
 | 
			
		||||
                key.fingerprint,
 | 
			
		||||
                key.key_type,
 | 
			
		||||
                key.size.to_string(),
 | 
			
		||||
                key.created_at
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    table.printstd();
 | 
			
		||||
}
 | 
			
		||||
@@ -30,63 +30,4 @@ pub mod server_api {
 | 
			
		||||
        Ok(servers.into_iter().map(Dynamic::from).collect())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(name = "print_servers_table")]
 | 
			
		||||
    pub fn print_servers_table(servers: Array) {
 | 
			
		||||
        let mut table = Table::new();
 | 
			
		||||
        table.add_row(row![
 | 
			
		||||
            "Server Number",
 | 
			
		||||
            "Server Name",
 | 
			
		||||
            "Server IP",
 | 
			
		||||
            "Product",
 | 
			
		||||
            "Datacenter",
 | 
			
		||||
            "Status"
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        for server_dynamic in servers {
 | 
			
		||||
            let server: Server = server_dynamic
 | 
			
		||||
                .try_cast::<Server>()
 | 
			
		||||
                .expect("could not cast to server");
 | 
			
		||||
            table.add_row(row![
 | 
			
		||||
                server.server_number,
 | 
			
		||||
                server.server_name,
 | 
			
		||||
                server.server_ip,
 | 
			
		||||
                server.product,
 | 
			
		||||
                server.dc,
 | 
			
		||||
                server.status
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        table.printstd();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(name = "print_server_details")]
 | 
			
		||||
    pub fn print_server_details(server: Server) {
 | 
			
		||||
        let mut table = Table::new();
 | 
			
		||||
        table.add_row(row!["Property", "Value"]);
 | 
			
		||||
        table.add_row(row!["Server Number", server.server_number]);
 | 
			
		||||
        table.add_row(row!["Server Name", server.server_name]);
 | 
			
		||||
        table.add_row(row!["Server IP", server.server_ip]);
 | 
			
		||||
        table.add_row(row!["IPv6 Network", server.server_ipv6_net]);
 | 
			
		||||
        table.add_row(row!["Product", server.product]);
 | 
			
		||||
        table.add_row(row!["Datacenter", server.dc]);
 | 
			
		||||
        table.add_row(row!["Traffic", server.traffic]);
 | 
			
		||||
        table.add_row(row!["Status", server.status]);
 | 
			
		||||
        table.add_row(row!["Cancelled", server.cancelled]);
 | 
			
		||||
        table.add_row(row!["Paid Until", server.paid_until]);
 | 
			
		||||
        table.add_row(row!["Reset", server.reset.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row!["Rescue", server.rescue.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row!["VNC", server.vnc.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row!["Windows", server.windows.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row!["Plesk", server.plesk.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row!["cPanel", server.cpanel.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row!["WOL", server.wol.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row!["Hot Swap", server.hot_swap.unwrap_or(false)]);
 | 
			
		||||
        table.add_row(row![
 | 
			
		||||
            "Linked Storagebox",
 | 
			
		||||
            server
 | 
			
		||||
                .linked_storagebox
 | 
			
		||||
                .map_or("N/A".to_string(), |id| id.to_string())
 | 
			
		||||
        ]);
 | 
			
		||||
        table.printstd();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
use crate::api::{models::SshKey, Client};
 | 
			
		||||
use prettytable::{row, Table};
 | 
			
		||||
use rhai::{plugin::*, Array, Dynamic, Engine};
 | 
			
		||||
use crate::api::{Client, models::SshKey};
 | 
			
		||||
use prettytable::{Table, row};
 | 
			
		||||
use rhai::{Array, Dynamic, Engine, plugin::*};
 | 
			
		||||
 | 
			
		||||
pub fn register(engine: &mut Engine) {
 | 
			
		||||
    let ssh_keys_module = exported_module!(ssh_keys_api);
 | 
			
		||||
@@ -62,10 +62,10 @@ pub mod ssh_keys_api {
 | 
			
		||||
            .map_err(|e| e.to_string().into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(name = "print_ssh_keys_table")]
 | 
			
		||||
    pub fn print_ssh_keys_table(keys: Array) {
 | 
			
		||||
    #[rhai_fn(name = "pretty_print")]
 | 
			
		||||
    pub fn pretty_print_ssh_keys(keys: Array) {
 | 
			
		||||
        let mut table = Table::new();
 | 
			
		||||
        table.add_row(row![
 | 
			
		||||
        table.add_row(row![b =>
 | 
			
		||||
            "Name",
 | 
			
		||||
            "Fingerprint",
 | 
			
		||||
            "Type",
 | 
			
		||||
@@ -73,19 +73,17 @@ pub mod ssh_keys_api {
 | 
			
		||||
            "Created At"
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        for key_dynamic in keys {
 | 
			
		||||
            let key: SshKey = key_dynamic
 | 
			
		||||
                .try_cast::<SshKey>()
 | 
			
		||||
                .expect("could not cast to SshKey");
 | 
			
		||||
        for key_dyn in keys {
 | 
			
		||||
            if let Some(key) = key_dyn.try_cast::<SshKey>() {
 | 
			
		||||
                table.add_row(row![
 | 
			
		||||
                    key.name,
 | 
			
		||||
                    key.fingerprint,
 | 
			
		||||
                    key.key_type,
 | 
			
		||||
                key.size,
 | 
			
		||||
                    key.size.to_string(),
 | 
			
		||||
                    key.created_at
 | 
			
		||||
                ]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        table.printstd();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user