matroskin 0.1.0

whatsminer api client lib
Documentation
//! Implement `set.system.net_config` command
//!
//! Configures the device's network settings. The device will reboot
//! to apply the new configuration.
//!
//! - Command: [SetSystemNetConfig]
//! - ApiDoc: <https://apidoc.whatsminer.com/#api-System-system_net_config>
use core::str;

use serde::Serialize;

use crate::{
    command::{Command, CommandLevel},
    error::Result,
};

/// Static IP network configuration.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct StaticNetConfig {
    /// The static IP address.
    pub ip: String,
    /// The subnet mask.
    pub netmask: String,
    /// The gateway address.
    pub gateway: String,
    /// The DNS server address.
    pub dns: String,
}

/// Parameter for [SetSystemNetConfig].
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NetConfigParam {
    /// Enable DHCP.
    Dhcp,
    /// Configure a static IP address.
    Static(StaticNetConfig),
}

impl serde::Serialize for NetConfigParam {
    fn serialize<S: serde::Serializer>(
        &self,
        serializer: S,
    ) -> std::result::Result<S::Ok, S::Error> {
        match self {
            Self::Dhcp => "dhcp".serialize(serializer),
            Self::Static(config) => config.serialize(serializer),
        }
    }
}

/// This command represents the `set.system.net_config` operation.
///
/// Configures the device's network settings. The device will reboot
/// to apply the new configuration.
///
/// - ApiDoc: <https://apidoc.whatsminer.com/#api-System-system_net_config>
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SetSystemNetConfig {
    /// Network configuration parameter
    pub config: NetConfigParam,
}

impl SetSystemNetConfig {
    /// Create a new `set.system.net_config` command with specified configuration.
    pub fn new(config: NetConfigParam) -> Self {
        Self { config }
    }

    /// Configure network to use DHCP.
    pub fn dhcp() -> Self {
        Self {
            config: NetConfigParam::Dhcp,
        }
    }

    /// Configure network to use static IP settings.
    pub fn static_ip(config: StaticNetConfig) -> Self {
        Self {
            config: NetConfigParam::Static(config),
        }
    }
}

impl Command for SetSystemNetConfig {
    type Response = String;
    const CMD_NAME: &'static str = "set.system.net_config";
    const LEVEL: CommandLevel = CommandLevel::Secured;

    fn params(&self) -> Result<Option<impl serde::Serialize>> {
        Ok(Some(&self.config))
    }
}

#[cfg(test)]
mod tests {
    use crate::account::Account;
    use crate::auth_data::AuthData;

    use super::*;

    const RESPONSE_JSON: &str =
        r#"{"code":0,"when":1672281726,"msg":"ok","desc":"set.system.net_config"}"#;

    #[test]
    fn request_dhcp_mode() {
        let cmd = SetSystemNetConfig::dhcp();
        assert!(cmd.is_secured());
        assert!(!cmd.is_encrypted());

        let auth = AuthData::new::<SetSystemNetConfig>(Account::Super, "pass", "salt").unwrap();
        let encoded = cmd.encode(Some(auth)).unwrap();
        let json: serde_json::Value = serde_json::from_slice(&encoded.0).unwrap();

        assert_eq!(json["cmd"], "set.system.net_config");
        assert_eq!(json["param"], "dhcp");
        assert_eq!(json["account"], "super");
        assert!(json["ts"].as_u64().unwrap() > 0);
        assert_eq!(json["token"].as_str().unwrap().len(), 8);
    }

    #[test]
    fn request_static_mode() {
        let cmd = SetSystemNetConfig::static_ip(StaticNetConfig {
            ip: "192.168.2.22".to_string(),
            netmask: "255.255.255.0".to_string(),
            gateway: "192.168.2.1".to_string(),
            dns: "192.168.2.1".to_string(),
        });

        let auth = AuthData::new::<SetSystemNetConfig>(Account::Super, "pass", "salt").unwrap();
        let encoded = cmd.encode(Some(auth)).unwrap();
        let json: serde_json::Value = serde_json::from_slice(&encoded.0).unwrap();

        assert_eq!(json["cmd"], "set.system.net_config");
        assert_eq!(json["param"]["ip"], "192.168.2.22");
    }

    #[test]
    fn request_without_auth_omits_token() {
        let cmd = SetSystemNetConfig::dhcp();
        let encoded = cmd.encode(None).unwrap();
        let json: serde_json::Value = serde_json::from_slice(&encoded.0).unwrap();
        assert!(json.get("token").is_none());
        assert_eq!(json["param"], "dhcp");
    }

    #[test]
    fn deserialize_response_fixture() {
        let response = SetSystemNetConfig::parse_response(RESPONSE_JSON.as_bytes()).unwrap();
        assert!(response.code.is_success());
        assert_eq!(response.desc, "set.system.net_config");
        assert_eq!(response.when, 1672281726);
        assert_eq!(response.msg, "ok");
    }
}