mechutil 0.8.0

Utility structures and functions for mechatronics applications.
Documentation
//
// Copyright (C) 2024 - 2025 Automated Design Corp. All Rights Reserved.
//

//! Standard CLI argument parsing for external modules.

use super::error::IpcError;

/// Parsed command-line arguments common to all external modules.
pub struct ModuleArgs {
    /// IPC server address (from `--ipc-address`, default `"127.0.0.1:9100"`)
    pub ipc_address: String,
    /// Module instance name (from `--module-name`, required)
    pub module_name: String,
    /// Optional JSON configuration string (from `--config` / `-c`)
    pub config_json: Option<String>,
}

impl ModuleArgs {
    /// Parse standard module arguments from `std::env::args()`.
    ///
    /// Recognised flags:
    /// - `--ipc-address <addr>` (default `127.0.0.1:9100`)
    /// - `--module-name <name>` (required)
    /// - `--config <json>` or `-c <json>` (optional)
    pub fn from_env() -> Result<Self, IpcError> {
        let args: Vec<String> = std::env::args().collect();
        let mut ipc_address = "127.0.0.1:9100".to_string();
        let mut module_name: Option<String> = None;
        let mut config_json: Option<String> = None;

        let mut i = 1; // skip argv[0]
        while i < args.len() {
            match args[i].as_str() {
                "--ipc-address" => {
                    i += 1;
                    if i >= args.len() {
                        return Err(IpcError::InvalidMessage("--ipc-address requires a value".into()));
                    }
                    ipc_address = args[i].clone();
                }
                "--module-name" => {
                    i += 1;
                    if i >= args.len() {
                        return Err(IpcError::InvalidMessage("--module-name requires a value".into()));
                    }
                    module_name = Some(args[i].clone());
                }
                "--config" | "-c" => {
                    i += 1;
                    if i >= args.len() {
                        return Err(IpcError::InvalidMessage("--config requires a value".into()));
                    }
                    config_json = Some(args[i].clone());
                }
                _ => {
                    // Ignore unknown args for forward compatibility
                }
            }
            i += 1;
        }

        let module_name = module_name.ok_or_else(|| {
            IpcError::InvalidMessage("--module-name is required".into())
        })?;

        Ok(Self {
            ipc_address,
            module_name,
            config_json,
        })
    }
}