use serde::{Deserialize, Serialize};
use super::{
results::ScreenContentResult,
types::{BufferId, Position, ScreenFormat, WireLayoutChangeKind, WireLayoutInfo},
};
pub const MODE_CHANGED: &str = "notification/mode_changed";
pub const CURSOR_MOVED: &str = "notification/cursor_moved";
pub const BUFFER_MODIFIED: &str = "notification/buffer_modified";
pub const RENDER_COMPLETE: &str = "notification/render_complete";
pub const LOG_ENTRY: &str = "notification/log_entry";
pub const DETACH: &str = "notification/detach";
pub const LAYOUT_CHANGED: &str = "notification/layout_changed";
pub const OPTION_CHANGED: &str = "notification/option_changed";
pub const CMDLINE_CHANGED: &str = "notification/cmdline_changed";
pub const CAPTURE_REQUEST: &str = "tui/capture-request";
pub const CAPTURE_RESPONSE: &str = "tui/capture-response";
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum LogSource {
#[default]
Server,
Client,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Trace = 0,
Debug = 1,
#[default]
Info = 2,
Warn = 3,
Error = 4,
}
impl LogLevel {
#[must_use]
pub fn from_str_lossy(s: &str) -> Self {
match s.to_lowercase().as_str() {
"trace" => Self::Trace,
"debug" => Self::Debug,
"warn" | "warning" => Self::Warn,
"error" => Self::Error,
_ => Self::Info,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogEntryPayload {
pub timestamp: String,
pub level: LogLevel,
pub target: String,
pub message: String,
#[serde(default)]
pub source: LogSource,
}
impl LogEntryPayload {
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
LOG_ENTRY,
serde_json::to_value(self).expect("LogEntryPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModeChangedPayload {
pub mode: super::types::ModeInfo,
}
impl ModeChangedPayload {
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
MODE_CHANGED,
serde_json::to_value(self).expect("ModeChangedPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CursorMovedPayload {
pub buffer_id: BufferId,
pub position: Position,
}
impl CursorMovedPayload {
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
CURSOR_MOVED,
serde_json::to_value(self).expect("CursorMovedPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BufferModifiedPayload {
pub buffer_id: BufferId,
pub modified: bool,
}
impl BufferModifiedPayload {
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
BUFFER_MODIFIED,
serde_json::to_value(self).expect("BufferModifiedPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RenderCompletePayload {
}
impl RenderCompletePayload {
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
RENDER_COMPLETE,
serde_json::to_value(self).expect("RenderCompletePayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DetachPayload {
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
impl DetachPayload {
#[must_use]
pub const fn new() -> Self {
Self { reason: None }
}
#[must_use]
pub fn with_reason(reason: impl Into<String>) -> Self {
Self {
reason: Some(reason.into()),
}
}
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
DETACH,
serde_json::to_value(self).expect("DetachPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayoutChangedPayload {
pub kind: WireLayoutChangeKind,
pub layout: WireLayoutInfo,
}
impl LayoutChangedPayload {
#[must_use]
pub const fn new(kind: WireLayoutChangeKind, layout: WireLayoutInfo) -> Self {
Self { kind, layout }
}
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
LAYOUT_CHANGED,
serde_json::to_value(self).expect("LayoutChangedPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptionChangedPayload {
pub name: String,
pub value: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub window_id: Option<usize>,
}
impl OptionChangedPayload {
#[must_use]
pub fn global(name: impl Into<String>, value: serde_json::Value) -> Self {
Self {
name: name.into(),
value,
window_id: None,
}
}
#[must_use]
pub fn window(name: impl Into<String>, value: serde_json::Value, window_id: usize) -> Self {
Self {
name: name.into(),
value,
window_id: Some(window_id),
}
}
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
OPTION_CHANGED,
serde_json::to_value(self).expect("OptionChangedPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum WireCmdlinePrompt {
#[default]
Command,
SearchForward,
SearchBackward,
}
impl WireCmdlinePrompt {
#[must_use]
pub const fn char(self) -> char {
match self {
Self::Command => ':',
Self::SearchForward => '/',
Self::SearchBackward => '?',
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CmdlineChangedPayload {
pub visible: bool,
pub prompt: WireCmdlinePrompt,
pub input: String,
pub cursor: usize,
}
impl CmdlineChangedPayload {
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn show(prompt: WireCmdlinePrompt, input: String, cursor: usize) -> Self {
Self {
visible: true,
prompt,
input,
cursor,
}
}
#[must_use]
pub const fn hide() -> Self {
Self {
visible: false,
prompt: WireCmdlinePrompt::Command,
input: String::new(),
cursor: 0,
}
}
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
CMDLINE_CHANGED,
serde_json::to_value(self).expect("CmdlineChangedPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CaptureRequestPayload {
pub request_id: u64,
#[serde(default)]
pub format: ScreenFormat,
}
impl CaptureRequestPayload {
#[must_use]
pub const fn new(request_id: u64, format: ScreenFormat) -> Self {
Self { request_id, format }
}
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
CAPTURE_REQUEST,
serde_json::to_value(self).expect("CaptureRequestPayload serialization cannot fail"),
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CaptureResponsePayload {
pub request_id: u64,
pub result: ScreenContentResult,
}
impl CaptureResponsePayload {
#[must_use]
pub const fn new(request_id: u64, result: ScreenContentResult) -> Self {
Self { request_id, result }
}
#[must_use]
pub fn into_notification(self) -> super::messages::RpcNotification {
super::messages::RpcNotification::new(
CAPTURE_RESPONSE,
serde_json::to_value(self).expect("CaptureResponsePayload serialization cannot fail"),
)
}
}
#[cfg(test)]
#[path = "notifications_tests.rs"]
mod tests;