1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
7#[serde(tag = "type", content = "payload", rename_all = "snake_case")]
8pub enum LogAvailability {
9 Headless {
10 stdout_path: PathBuf,
11 stderr_path: PathBuf,
12 },
13 TmuxPaneSnapshot,
14 Unavailable {
15 reason: LogsUnavailableReason,
16 },
17}
18
19#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
20#[serde(rename_all = "snake_case")]
21pub enum LogsUnavailableReason {
22 TmuxTarget,
23 CaptureDisabled,
24 PaneUnavailable,
25 PipeInUse,
26 RecorderFailed,
27}
28
29impl LogsUnavailableReason {
30 pub const fn as_str(self) -> &'static str {
31 match self {
32 Self::TmuxTarget => "tmux_target",
33 Self::CaptureDisabled => "capture_disabled",
34 Self::PaneUnavailable => "pane_unavailable",
35 Self::PipeInUse => "pipe_in_use",
36 Self::RecorderFailed => "recorder_failed",
37 }
38 }
39}
40
41#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
42pub struct CaptureRequest {
43 pub session_id: Uuid,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub scrollback_lines: Option<u32>,
46}
47
48#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
49pub struct PaneSnapshot {
50 pub content: String,
51 pub captured_at_ms: u64,
52 pub scrollback_lines_requested: u32,
53 pub scrollback_lines_included: u32,
54 pub pane_history_lines: u32,
55}
56
57#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
58#[non_exhaustive]
59#[serde(tag = "type", content = "payload", rename_all = "snake_case")]
60pub enum CaptureError {
61 NotATmuxTarget,
62 PaneUnavailable,
63 SessionMissing,
64 TmuxNotAvailable,
65 CapturePaneFailed { stderr: String },
66}
67
68#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
69#[non_exhaustive]
70#[serde(tag = "status", content = "payload", rename_all = "snake_case")]
71pub enum CaptureResponse {
72 Captured(PaneSnapshot),
73 Failed(CaptureError),
74}
75
76impl CaptureResponse {
77 pub fn into_result(self) -> Result<PaneSnapshot, CaptureError> {
78 match self {
79 Self::Captured(snapshot) => Ok(snapshot),
80 Self::Failed(error) => Err(error),
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::LogsUnavailableReason;
88
89 #[test]
90 fn logs_unavailable_reason_as_str_matches_serde_snake_case() {
91 assert_eq!(LogsUnavailableReason::TmuxTarget.as_str(), "tmux_target");
92 assert_eq!(
93 LogsUnavailableReason::CaptureDisabled.as_str(),
94 "capture_disabled"
95 );
96 assert_eq!(
97 LogsUnavailableReason::PaneUnavailable.as_str(),
98 "pane_unavailable"
99 );
100 assert_eq!(LogsUnavailableReason::PipeInUse.as_str(), "pipe_in_use");
101 assert_eq!(
102 LogsUnavailableReason::RecorderFailed.as_str(),
103 "recorder_failed"
104 );
105 }
106}