Skip to main content

ironflow_cli/commands/
logs.rs

1//! Log streaming via SSE.
2
3use std::io::Write as _;
4
5use anyhow::Result;
6use clap::Args;
7use futures_util::StreamExt;
8use ironflow_sdk::IronflowClient;
9use uuid::Uuid;
10
11/// Arguments for the `logs` command.
12#[derive(Debug, Args)]
13pub struct LogsArgs {
14    /// Run UUID to stream logs for.
15    pub run_id: Uuid,
16    /// Keep streaming until the run reaches a terminal state.
17    #[arg(long)]
18    pub follow: bool,
19}
20
21/// Terminal event types that signal the run is done.
22const TERMINAL_EVENTS: &[&str] = &["run_completed", "run_failed", "run_cancelled"];
23
24/// Execute the `logs` command.
25///
26/// # Errors
27///
28/// Returns an error on SSE connection failure.
29pub async fn execute(client: &IronflowClient, args: &LogsArgs, json_mode: bool) -> Result<()> {
30    let mut stream = client.events(Some(args.run_id), None).await?;
31    let mut out = std::io::stdout().lock();
32
33    while let Some(event) = stream.next().await {
34        match event {
35            Ok(ev) => {
36                if json_mode {
37                    let obj = serde_json::json!({
38                        "event": ev.event_type,
39                        "data": ev.data,
40                    });
41                    writeln!(out, "{}", serde_json::to_string(&obj)?)?;
42                } else {
43                    writeln!(out, "[{}] {}", ev.event_type, ev.data)?;
44                }
45
46                if TERMINAL_EVENTS.contains(&ev.event_type.as_str()) {
47                    break;
48                }
49
50                if !args.follow {
51                    break;
52                }
53            }
54            Err(e) => {
55                return Err(anyhow::anyhow!("SSE stream error: {e}"));
56            }
57        }
58    }
59
60    Ok(())
61}