robotrt-cli 0.1.0-beta.2

RobotRT modular robotics runtime and middleware components.
use crate::helpers::{
    first_positional, has_flag, option_value, parse_u64_option, resolve_runtime_endpoint,
};
use crate::gateway::{
    STATUS_OP_SERVICE_CALL, STATUS_SERVICE_NAME, StatusServiceResponse,
    build_service_call_request, make_udp_service_client, next_request_id,
    validate_response,
};

const DEFAULT_DAEMON_ENDPOINT: &str = "127.0.0.1:7588";

pub fn service_call(args: &[String]) -> Result<(), String> {
    let service = first_positional(args)
        .ok_or_else(|| String::from("missing service name"))?;
    let request_payload = option_value(args, "--request")
        .unwrap_or_else(|| String::new());
    let timeout_ms = parse_u64_option(args, "--timeout-ms", 1000)?;
    let json = has_flag(args, "--json");
    let endpoint = resolve_runtime_endpoint(
        args,
        DEFAULT_DAEMON_ENDPOINT,
        "service call requires --endpoint in embedded mode",
    )?;

    let client = make_udp_service_client(endpoint.clone(), timeout_ms)?;
    let request_id = next_request_id();
    let request = build_service_call_request(
        service.clone(),
        request_payload.clone(),
        Some(timeout_ms),
    );

    let response: StatusServiceResponse = client
        .call_json(STATUS_SERVICE_NAME, request_id, &request)
        .map_err(|err| format!("service call to {endpoint} failed: {err}"))?;

    validate_response(&response, request_id, STATUS_OP_SERVICE_CALL)?;
    let result = response.service_call.ok_or_else(|| {
        format!("service call response from {endpoint} missing service_call payload")
    })?;

    if json {
        let payload = serde_json::json!({
            "api_version": "robotrt.service.call.v1",
            "source": {
                "mode": "remote_service",
                "service": STATUS_SERVICE_NAME,
                "endpoint": endpoint,
                "timeout_ms": timeout_ms,
            },
            "request": {
                "service": service,
                "payload": request_payload,
            },
            "result": result,
        });
        println!(
            "{}",
            serde_json::to_string_pretty(&payload)
                .map_err(|err| format!("serialize service call json failed: {err}"))?
        );
        return Ok(());
    }

    println!("RobotRT Service Call");
    println!("endpoint: {endpoint}");
    println!("service: {}", result.service);
    println!("accepted: {}", result.accepted);
    println!("response: {}", result.response.as_deref().unwrap_or("-"));
    println!("error: {}", result.error.as_deref().unwrap_or("-"));

    Ok(())
}