tnnl 0.1.17

tnnl gives you full control over whether and when your IoT devices should be reachable from the internet
use serde::{Deserialize, Serialize};
use serde_json::{Map, Number, Value};
use uuid::Uuid;

use crate::{apperror::AppError, session::LoginInfo};

#[derive(Serialize, Deserialize, Debug)]
pub struct Command {
    #[serde(rename = "commandId")]
    pub command_id: u32,
    #[serde(rename = "command")]
    pub command: String,
    #[serde(rename = "data")]
    pub data: Value,
}

pub struct CommandWithoutId {
    pub command: String,
    pub data: Value,
}

impl Command {
    pub fn to_json_buffer(&self) -> Result<Vec<u8>, AppError> {
        serde_json::to_vec(self).map_err(|_| AppError::new("serialization failed"))
    }
}

impl CommandWithoutId {
    pub fn to_command(self: CommandWithoutId, command_id: u32) -> Command {
        Command {
            command: self.command,
            data: self.data,
            command_id,
        }
    }

    pub fn create_send_info(
        login_info: LoginInfo,
        client: String,
        protocol_version: u32,
    ) -> CommandWithoutId {
        let mut data = Map::new();

        let client_id = login_info
            .client_id
            .unwrap_or_else(|| Uuid::new_v4().to_string());
        let client_name = login_info
            .client_name
            .unwrap_or_else(|| "rust-client".to_string());

        data.insert("name".to_string(), Value::String(client_name));
        data.insert("id".to_string(), Value::String(client_id));
        data.insert("token".to_string(), Value::String(login_info.token));
        if let Some(mac) = login_info.mac {
            data.insert("mac".to_string(), Value::String(mac));
        }
        data.insert("client".to_string(), Value::String(client));
        data.insert(
            "protocolVersion".to_string(),
            Value::Number(Number::from(protocol_version)),
        );

        if let Some(package_version) = option_env!("CARGO_PKG_VERSION") {
            data.insert(
                "packageVersion".to_string(),
                Value::String(package_version.to_string()),
            );
        }
        if let Some(package_name) = option_env!("CARGO_PKG_NAME") {
            data.insert(
                "packageName".to_string(),
                Value::String(package_name.to_string()),
            );
        }

        CommandWithoutId {
            command: "info".to_string(),
            data: serde_json::Value::Object(data),
        }
    }

    pub fn create_post_upload(data: Map<String, Value>) -> CommandWithoutId {
        CommandWithoutId {
            command: "postUpload".to_string(),
            data: serde_json::Value::Object(data),
        }
    }

    pub fn create_get_download() -> CommandWithoutId {
        let data = Map::new();
        CommandWithoutId {
            command: "getDownload".to_string(),
            data: serde_json::Value::Object(data),
        }
    }

    pub fn create_get_site_info() -> CommandWithoutId {
        let data = Map::new();
        CommandWithoutId {
            command: "getSiteInfo".to_string(),
            data: serde_json::Value::Object(data),
        }
    }
}