unifly-api 0.9.0

Async Rust client, reactive data layer, and domain model for UniFi controller APIs
Documentation
use crate::command::{Command, CommandResult};
use crate::core_error::CoreError;

use super::{
    CommandContext, client_mac, device_mac, require_integration, require_session, require_uuid,
};

#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
pub(super) async fn route(ctx: &CommandContext, cmd: Command) -> Result<CommandResult, CoreError> {
    let store = ctx.store.as_ref();
    let integration = ctx.integration.as_ref();
    let session = ctx.session.as_ref();
    let site_id = ctx.site_id;

    match cmd {
        Command::AdoptDevice {
            mac,
            ignore_device_limit,
        } => {
            if let (Some(ic), Some(sid)) = (integration, site_id) {
                ic.adopt_device(&sid, mac.as_str(), ignore_device_limit)
                    .await?;
            } else {
                let session = require_session(session)?;
                session.adopt_device(mac.as_str()).await?;
            }
            Ok(CommandResult::Ok)
        }
        Command::RestartDevice { id } => {
            if let (Some(ic), Some(sid)) = (integration, site_id) {
                let device_uuid = require_uuid(&id)?;
                ic.device_action(&sid, &device_uuid, "RESTART").await?;
            } else {
                let session = require_session(session)?;
                let mac = device_mac(store, &id)?;
                session.restart_device(mac.as_str()).await?;
            }
            Ok(CommandResult::Ok)
        }
        Command::LocateDevice { mac, enable } => {
            if let (Some(ic), Some(sid)) = (integration, site_id) {
                let device =
                    store
                        .device_by_mac(&mac)
                        .ok_or_else(|| CoreError::DeviceNotFound {
                            identifier: mac.to_string(),
                        })?;
                let device_uuid = require_uuid(&device.id)?;
                let action = if enable { "LOCATE_ON" } else { "LOCATE_OFF" };
                ic.device_action(&sid, &device_uuid, action).await?;
            } else {
                let session = require_session(session)?;
                session.locate_device(mac.as_str(), enable).await?;
            }
            Ok(CommandResult::Ok)
        }
        Command::UpgradeDevice { mac, firmware_url } => {
            let session = require_session(session)?;
            session
                .upgrade_device(mac.as_str(), firmware_url.as_deref())
                .await?;
            Ok(CommandResult::Ok)
        }
        Command::RemoveDevice { id } => {
            let (ic, sid) = require_integration(integration, site_id, "RemoveDevice")?;
            let device_uuid = require_uuid(&id)?;
            ic.remove_device(&sid, &device_uuid).await?;
            Ok(CommandResult::Ok)
        }
        Command::ProvisionDevice { mac } => {
            let session = require_session(session)?;
            session.provision_device(mac.as_str()).await?;
            Ok(CommandResult::Ok)
        }
        Command::SpeedtestDevice => {
            let session = require_session(session)?;
            session.speedtest().await?;
            Ok(CommandResult::Ok)
        }
        Command::PowerCyclePort {
            device_id,
            port_idx,
        } => {
            let (ic, sid) = require_integration(integration, site_id, "PowerCyclePort")?;
            let device_uuid = require_uuid(&device_id)?;
            ic.port_action(&sid, &device_uuid, port_idx, "POWER_CYCLE")
                .await?;
            Ok(CommandResult::Ok)
        }
        Command::BlockClient { mac } => {
            if let (Some(ic), Some(sid)) = (integration, site_id) {
                let client =
                    store
                        .client_by_mac(&mac)
                        .ok_or_else(|| CoreError::ClientNotFound {
                            identifier: mac.to_string(),
                        })?;
                let client_uuid = require_uuid(&client.id)?;
                ic.client_action(&sid, &client_uuid, "BLOCK").await?;
            } else {
                let session = require_session(session)?;
                session.block_client(mac.as_str()).await?;
            }
            Ok(CommandResult::Ok)
        }
        Command::UnblockClient { mac } => {
            if let (Some(ic), Some(sid)) = (integration, site_id) {
                let client =
                    store
                        .client_by_mac(&mac)
                        .ok_or_else(|| CoreError::ClientNotFound {
                            identifier: mac.to_string(),
                        })?;
                let client_uuid = require_uuid(&client.id)?;
                ic.client_action(&sid, &client_uuid, "UNBLOCK").await?;
            } else {
                let session = require_session(session)?;
                session.unblock_client(mac.as_str()).await?;
            }
            Ok(CommandResult::Ok)
        }
        Command::KickClient { mac } => {
            if let (Some(ic), Some(sid)) = (integration, site_id) {
                let client =
                    store
                        .client_by_mac(&mac)
                        .ok_or_else(|| CoreError::ClientNotFound {
                            identifier: mac.to_string(),
                        })?;
                let client_uuid = require_uuid(&client.id)?;
                ic.client_action(&sid, &client_uuid, "RECONNECT").await?;
            } else {
                let session = require_session(session)?;
                session.kick_client(mac.as_str()).await?;
            }
            Ok(CommandResult::Ok)
        }
        Command::ForgetClient { mac } => {
            let session = require_session(session)?;
            session.forget_client(mac.as_str()).await?;
            Ok(CommandResult::Ok)
        }
        Command::AuthorizeGuest {
            client_id,
            time_limit_minutes,
            data_limit_mb,
            rx_rate_kbps,
            tx_rate_kbps,
        } => {
            let session = require_session(session)?;
            let mac = client_mac(store, &client_id)?;
            let minutes = time_limit_minutes.unwrap_or(60);
            #[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
            {
                session
                    .authorize_guest(
                        mac.as_str(),
                        minutes,
                        tx_rate_kbps.map(|r| r as u32),
                        rx_rate_kbps.map(|r| r as u32),
                        data_limit_mb.map(|m| m as u32),
                    )
                    .await?;
            }
            Ok(CommandResult::Ok)
        }
        Command::UnauthorizeGuest { client_id } => {
            let session = require_session(session)?;
            let mac = client_mac(store, &client_id)?;
            session.unauthorize_guest(mac.as_str()).await?;
            Ok(CommandResult::Ok)
        }
        Command::SetClientFixedIp {
            mac,
            ip,
            network_id,
        } => {
            let session = require_session(session)?;
            session
                .set_client_fixed_ip(mac.as_str(), &ip.to_string(), &network_id.to_string())
                .await?;
            Ok(CommandResult::Ok)
        }
        Command::RemoveClientFixedIp { mac, network_id } => {
            let session = require_session(session)?;
            let network_id = network_id.as_ref().map(ToString::to_string);
            session
                .remove_client_fixed_ip(mac.as_str(), network_id.as_deref())
                .await?;
            Ok(CommandResult::Ok)
        }
        _ => unreachable!("device_client::route received non-device/client command"),
    }
}