use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct Position {
pub line: usize,
pub column: usize,
}
impl Position {
#[must_use]
pub const fn new(line: usize, column: usize) -> Self {
Self { line, column }
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BufferId(pub usize);
impl From<usize> for BufferId {
fn from(id: usize) -> Self {
Self(id)
}
}
impl From<BufferId> for usize {
fn from(id: BufferId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(transparent)]
pub struct WireWindowId(pub usize);
impl From<usize> for WireWindowId {
fn from(id: usize) -> Self {
Self(id)
}
}
impl From<WireWindowId> for usize {
fn from(id: WireWindowId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(transparent)]
pub struct WireLayerId(pub usize);
impl From<usize> for WireLayerId {
fn from(id: usize) -> Self {
Self(id)
}
}
impl From<WireLayerId> for usize {
fn from(id: WireLayerId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct WireRect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
impl WireRect {
#[must_use]
pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
Self {
x,
y,
width,
height,
}
}
#[must_use]
pub const fn full_screen(width: u16, height: u16) -> Self {
Self {
x: 0,
y: 0,
width,
height,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum WireZone {
#[default]
Tiled,
Float,
Overlay,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum WireSplitDirection {
Horizontal,
Vertical,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum WireLayoutChangeKind {
Split {
new_window: WireWindowId,
direction: WireSplitDirection,
},
Close {
closed_window: WireWindowId,
new_focus: Option<WireWindowId>,
},
Focus {
from: Option<WireWindowId>,
to: WireWindowId,
},
Resize {
window: WireWindowId,
},
Equalize,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WireWindowPlacement {
pub window_id: WireWindowId,
pub layer_id: WireLayerId,
pub zone: WireZone,
pub bounds: WireRect,
pub z_order: u16,
pub visible: bool,
pub focusable: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub buffer_id: Option<BufferId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct WireLayoutInfo {
#[serde(default)]
pub screen: WireRect,
#[serde(default)]
pub windows: Vec<WireWindowPlacement>,
#[serde(skip_serializing_if = "Option::is_none")]
pub focused_window: Option<WireWindowId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub active_layer: Option<WireLayerId>,
#[serde(default)]
pub window_count: usize,
}
impl WireLayoutInfo {
#[must_use]
pub fn single_window(width: u16, height: u16, buffer_id: Option<BufferId>) -> Self {
let window = WireWindowPlacement {
window_id: WireWindowId(0),
layer_id: WireLayerId(0),
zone: WireZone::Tiled,
bounds: WireRect::full_screen(width, height),
z_order: 0,
visible: true,
focusable: true,
buffer_id,
};
Self {
screen: WireRect::full_screen(width, height),
windows: vec![window],
focused_window: Some(WireWindowId(0)),
active_layer: Some(WireLayerId(0)),
window_count: 1,
}
}
#[must_use]
pub const fn is_multi_window(&self) -> bool {
self.windows.len() > 1
}
#[must_use]
pub fn focused(&self) -> Option<&WireWindowPlacement> {
self.focused_window
.and_then(|id| self.windows.iter().find(|w| w.window_id == id))
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SelectionMode {
#[default]
Character,
Line,
Block,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ScreenFormat {
RawAnsi,
#[default]
PlainText,
CellGrid,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ModeInfo {
pub focus: String,
pub edit_mode: String,
pub sub_mode: String,
pub display: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CursorInfo {
pub line: usize,
pub column: usize,
}
impl From<Position> for CursorInfo {
fn from(pos: Position) -> Self {
Self {
line: pos.line,
column: pos.column,
}
}
}
impl From<CursorInfo> for Position {
fn from(info: CursorInfo) -> Self {
Self {
line: info.line,
column: info.column,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SelectionInfo {
pub active: bool,
pub mode: SelectionMode,
pub anchor: Position,
pub cursor: Position,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BufferInfo {
pub id: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_path: Option<String>,
pub modified: bool,
pub line_count: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub readonly: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub codec_metadata: Option<CodecMetadataWire>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CodecMetadataWire {
pub codec_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub line_ending: Option<String>,
#[serde(default, skip_serializing_if = "is_false")]
pub has_bom: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScreenInfo {
pub width: u16,
pub height: u16,
pub active_buffer_id: BufferId,
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_id: Option<WireWindowId>,
pub window_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WindowInfo {
pub id: WireWindowId,
pub buffer_id: BufferId,
pub is_active: bool,
pub cursor: Position,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CellInfo {
pub char: char,
#[serde(skip_serializing_if = "Option::is_none")]
pub fg: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bg: Option<String>,
#[serde(default, skip_serializing_if = "is_false")]
pub bold: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub italic: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub underline: bool,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_false(b: &bool) -> bool {
!*b
}
impl Default for CellInfo {
fn default() -> Self {
Self {
char: ' ',
fg: None,
bg: None,
bold: false,
italic: false,
underline: false,
}
}
}
#[cfg(test)]
#[path = "types_tests.rs"]
mod tests;