upcloud-rs 0.1.4

A pure Rust Upcloud API binding.
Documentation
use std::collections::HashMap;

use crate::{
    data::UpcloudServerRoot, UpcloudApi, UpcloudError, UpcloudLabelList,
    UpcloudServer,
};
use serde::Serialize;
use serde_json::{json, Value};

#[derive(Serialize, Debug)]
struct CreateInstanceConfig {
    region: String,
    plan: String,
    labels: Option<UpcloudLabelList>,
    #[serde(skip_serializing_if = "Option::is_none")]
    os_id: Option<String>,
}

/// Builder struct for creating instances.
///
/// A detailed documentation can be found at <https://developers.upcloud.com/1.3/8-servers/#create-server>
pub struct CreateInstanceBuilder {
    api: UpcloudApi,
    config: Value,
}

impl CreateInstanceBuilder {
    pub fn new<S1, S2, S3, S4, S5>(
        api: UpcloudApi,
        region_id: S1,
        plan_id: S2,
        os_id: S3,
        title: S4,
        hostname: S5,
    ) -> Self
    where
        S1: Into<String> + Serialize,
        S2: Into<String> + Serialize,
        S3: Into<String> + Serialize,
        S4: Into<String> + Serialize,
        S5: Into<String> + Serialize,
    {
        let mut instancebuilder = CreateInstanceBuilder {
            api,
            config: json!(    {
                "server":{
                    "title": title,
                    "zone": region_id,
                    "hostname": hostname,
                    "plan": plan_id,
                    "metadata": "yes"
                }
            }),
        };
        instancebuilder.config["server"]["storage_devices"] = json!({
            "storage_device": [
                {
                    "action": "clone",
                    "storage": os_id,
                    "title": title,
                    "tier": "maxiops"
                }
            ]
        });
        instancebuilder
    }

    /// The user-supplied, base64 encoded user data to attach to this instance.
    pub fn user_data<S>(mut self, user_data: S) -> Self
    where
        S: Into<String>,
    {
        self.config["server"]["user_data"] = Value::String(user_data.into());
        self
    }

    /// Labels to apply to the instance
    pub fn labels(mut self, labels: HashMap<String, String>) -> Self {
        let labels_vec: Vec<Value> = labels
            .iter()
            .map(|(k, v)| json!({"key": k.to_string(), "value": v.to_string()}))
            .collect();
        self.config["server"]["labels"]["label"] = Value::Array(labels_vec);
        self
    }

    #[cfg(feature = "blocking")]
    pub fn run(self) -> Result<UpcloudServer, UpcloudError> {
        let url = format!("https://api.Upcloud.com/1.3/server");
        Ok(self
            .api
            .post(&url, self.config)?
            .json::<UpcloudServerRoot>()?
            .server)
    }

    pub async fn run_async(self) -> Result<UpcloudServer, UpcloudError> {
        let url = format!("https://api.Upcloud.com/1.3/server");
        Ok(self
            .api
            .post_async(&url, self.config)
            .await?
            .json::<UpcloudServerRoot>()
            .await?
            .server)
    }
}