|
| 1 | +//! Runs the supervisor dashboard demo process for local three-end integration. |
| 2 | +
|
| 3 | +// Import the filesystem path type used by argument parsing. |
| 4 | +use std::path::PathBuf; |
| 5 | +// Import the supervisor runtime entry point. |
| 6 | +use rust_supervisor::runtime::supervisor::Supervisor; |
| 7 | + |
| 8 | +// Define the default demo configuration path. |
| 9 | +const DEFAULT_CONFIG_PATH: &str = "examples/config/supervisor.yaml"; |
| 10 | + |
| 11 | +// Define the shared demo result type. |
| 12 | +type DemoResult = Result<(), Box<dyn std::error::Error>>; |
| 13 | + |
| 14 | +// Use the Tokio runtime for the asynchronous demo process. |
| 15 | +#[tokio::main] |
| 16 | +/// Runs the long-lived supervisor demo process. |
| 17 | +async fn main() -> DemoResult { |
| 18 | + // Parse the optional configuration argument. |
| 19 | + let config_path = parse_config_path(std::env::args().skip(1))?; |
| 20 | + // Start from configuration so dashboard IPC and registration heartbeat run. |
| 21 | + let handle = Supervisor::start_from_config_file(&config_path).await?; |
| 22 | + // Query current state once to prove the supervisor is live. |
| 23 | + let current = handle.current_state().await?; |
| 24 | + // Print the current state for operator inspection. |
| 25 | + println!("{current:#?}"); |
| 26 | + // Print that the local demo session is now waiting. |
| 27 | + println!("demo supervisor running"); |
| 28 | + // Wait until the operator stops the demo process. |
| 29 | + tokio::signal::ctrl_c().await?; |
| 30 | + // Shut down the supervisor tree before dropping the runtime handle. |
| 31 | + handle.shutdown_tree("operator", "demo shutdown").await?; |
| 32 | + // Drop the handle so the dashboard IPC guard removes its socket. |
| 33 | + drop(handle); |
| 34 | + // Finish the demo successfully. |
| 35 | + Ok(()) |
| 36 | + // End the demo process. |
| 37 | +} |
| 38 | + |
| 39 | +/// Parses the demo configuration argument. |
| 40 | +/// |
| 41 | +/// # Arguments |
| 42 | +/// |
| 43 | +/// - `args`: Command-line arguments after the program name. |
| 44 | +/// |
| 45 | +/// # Returns |
| 46 | +/// |
| 47 | +/// Returns the configured path or the default demo path. |
| 48 | +fn parse_config_path(args: impl IntoIterator<Item = String>) -> Result<PathBuf, std::io::Error> { |
| 49 | + // Convert the incoming arguments into an iterator. |
| 50 | + let mut args = args.into_iter(); |
| 51 | + // Read the first optional argument. |
| 52 | + let first = args.next(); |
| 53 | + // Return the default path when no arguments are provided. |
| 54 | + if first.is_none() { |
| 55 | + // Return the default demo configuration path. |
| 56 | + return Ok(PathBuf::from(DEFAULT_CONFIG_PATH)); |
| 57 | + // End the empty argument branch. |
| 58 | + } |
| 59 | + // Extract the first argument after the empty case has returned. |
| 60 | + let first = first.expect("first argument should exist after empty check"); |
| 61 | + // Reject any unsupported argument name. |
| 62 | + if first != "--config" { |
| 63 | + // Return an unsupported argument error. |
| 64 | + return Err(invalid_input(format!("unknown argument: {first}"))); |
| 65 | + // End the unsupported argument branch. |
| 66 | + } |
| 67 | + // Require a path after the configuration flag. |
| 68 | + let path = require_config_path(args.next())?; |
| 69 | + // Reject trailing arguments so the demo stays deterministic. |
| 70 | + if let Some(extra) = args.next() { |
| 71 | + // Return an unsupported trailing argument error. |
| 72 | + return Err(invalid_input(format!("unknown argument: {extra}"))); |
| 73 | + // End the trailing argument branch. |
| 74 | + } |
| 75 | + // Return the explicit configuration path. |
| 76 | + Ok(PathBuf::from(path)) |
| 77 | + // End argument parsing. |
| 78 | +} |
| 79 | + |
| 80 | +/// Requires the value after the configuration flag. |
| 81 | +/// |
| 82 | +/// # Arguments |
| 83 | +/// |
| 84 | +/// - `path`: Optional path argument. |
| 85 | +/// |
| 86 | +/// # Returns |
| 87 | +/// |
| 88 | +/// Returns the explicit path or an invalid-input error. |
| 89 | +fn require_config_path(path: Option<String>) -> Result<String, std::io::Error> { |
| 90 | + // Convert the optional path into a typed result. |
| 91 | + let path = path.ok_or_else(|| invalid_input("--config requires a path"))?; |
| 92 | + // Return the validated path. |
| 93 | + Ok(path) |
| 94 | + // End configuration path validation. |
| 95 | +} |
| 96 | + |
| 97 | +/// Builds an invalid input error for argument parsing. |
| 98 | +/// |
| 99 | +/// # Arguments |
| 100 | +/// |
| 101 | +/// - `message`: Human-readable argument error. |
| 102 | +/// |
| 103 | +/// # Returns |
| 104 | +/// |
| 105 | +/// Returns a standard I/O error with invalid-input kind. |
| 106 | +fn invalid_input(message: impl Into<String>) -> std::io::Error { |
| 107 | + // Convert argument validation failures into a standard error type. |
| 108 | + std::io::Error::new(std::io::ErrorKind::InvalidInput, message.into()) |
| 109 | + // End invalid-input construction. |
| 110 | +} |
0 commit comments