robotrt-cli 0.1.0-beta.1

RobotRT modular robotics runtime and middleware components.
use crate::constants::DEFAULT_STATUS_REPORT_PATH;
use crate::demo::demo_status_snapshot;
use crate::helpers::{has_flag, option_value, parse_report_path};
use crate::status_api::{
    STATUS_SERVICE_API_VERSION, STATUS_SERVICE_NAME, make_udp_service_server, recv_status_request,
    validate_request,
};

use super::DEFAULT_STATUS_BIND_ADDR;

pub(super) fn status_serve(args: &[String]) -> Result<(), String> {
    let bind = option_value(args, "--bind").unwrap_or_else(|| DEFAULT_STATUS_BIND_ADDR.to_string());
    let source = option_value(args, "--source").unwrap_or_else(|| "demo".to_string());
    let report_path = parse_report_path(args, DEFAULT_STATUS_REPORT_PATH)?;
    let once = has_flag(args, "--once");
    let json = has_flag(args, "--json");

    if source != "demo" && source != "file" {
        return Err(format!(
            "unsupported --source value: {source} (expected demo|file)"
        ));
    }

    let server = make_udp_service_server(&bind)?;

    if json {
        let boot = serde_json::json!({
            "api_version": STATUS_SERVICE_API_VERSION,
            "mode": "status_serve",
            "service": STATUS_SERVICE_NAME,
            "bind": bind,
            "source": source,
            "once": once,
        });
        println!(
            "{}",
            serde_json::to_string_pretty(&boot)
                .map_err(|err| format!("serialize status server bootstrap failed: {err}"))?
        );
    } else {
        println!("RobotRT Status Serve");
        println!("bind: {}", bind);
        println!("source: {}", source);
        println!("once: {}", once);
    }

    loop {
        let incoming = recv_status_request(&server)?;
        let req = incoming.request;
        let request_id = incoming.request_id;
        let peer = incoming.peer;
        let service_name = incoming.service;

        if let Err(reason) = validate_request(&req) {
            server
                .reply_error(&service_name, request_id, peer, reason)
                .map_err(|err| format!("status server send error response failed: {err}"))?;
            if once {
                return Ok(());
            }
            continue;
        }

        let snapshot = if source == "demo" {
            Ok(demo_status_snapshot())
        } else {
            introspection_core::read_status_snapshot(&report_path).map_err(|err| {
                format!(
                    "read status snapshot from {} failed: {err}",
                    report_path.display()
                )
            })
        };

        match snapshot {
            Ok(snapshot) => {
                let payload = crate::status_api::success_response(request_id, snapshot);
                server
                    .reply_json(&service_name, request_id, peer, &payload)
                    .map_err(|err| format!("status server send response failed: {err}"))?;
            }
            Err(err) => {
                server
                    .reply_error(&service_name, request_id, peer, err)
                    .map_err(|err| format!("status server send error response failed: {err}"))?;
            }
        }

        if once {
            return Ok(());
        }
    }
}