1#![expect(clippy::pub_use, reason = "This seems to come from the `bon` crate")]
4
5pub type Colour = (f32, f32, f32, f32);
7
8#[derive(serde::Serialize, serde::Deserialize, bon::Builder, Clone, Copy, Debug)]
14#[non_exhaustive]
15pub struct Cell {
16 pub character: char,
18 pub coordinates: (u32, u32),
20 pub bg: Option<Colour>,
23 pub fg: Option<Colour>,
26}
27
28#[derive(serde::Serialize, serde::Deserialize, bon::Builder, Clone, Copy, Debug)]
30#[non_exhaustive]
31pub struct Pixel {
32 pub coordinates: (u32, u32),
36 pub color: Option<Colour>,
39}
40
41#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
43#[serde(rename_all = "snake_case")]
44#[non_exhaustive]
45pub enum PluginInputMessages {
46 #[serde(rename = "pty_update")]
48 PTYUpdate {
49 size: (u16, u16),
51 cells: Vec<Cell>,
53 cursor: (u16, u16),
55 },
56 #[serde(rename = "tty_resize")]
58 TTYResize {
59 width: u16,
61 height: u16,
63 },
64}
65
66#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
68#[serde(rename_all = "snake_case")]
69#[non_exhaustive]
70pub enum PluginOutputMessages {
71 OutputText {
73 text: String,
75 coordinates: (u32, u32),
77 bg: Option<Colour>,
79 fg: Option<Colour>,
81 },
82
83 OutputCells(Vec<Cell>),
86
87 OutputPixels(Vec<Pixel>),
89}
90
91#[expect(clippy::default_numeric_fallback, reason = "Tests aren't so strict")]
92#[cfg(test)]
93mod test {
94 use super::*;
95
96 #[test]
97 fn output_text() {
98 let expected = serde_json::json!(
99 {
100 "output_text": {
101 "text": "foo",
102 "coordinates": [1, 2],
103 "bg": null,
104 "fg": [0.1, 0.2, 0.3, 0.4],
105 }
106 }
107 );
108
109 let output = PluginOutputMessages::OutputText {
110 text: "foo".to_owned(),
111 coordinates: (1, 2),
112 bg: None,
113 fg: Some((0.1, 0.2, 0.3, 0.4)),
114 };
115
116 assert_eq!(
117 expected.to_string(),
118 serde_json::to_string(&output).unwrap()
119 );
120 }
121
122 #[test]
123 fn output_cells() {
124 let expected = serde_json::json!(
125 {
126 "output_cells": [{
127 "character": "f",
128 "coordinates": [1, 2],
129 "bg": null,
130 "fg": [0.1, 0.2, 0.3, 0.4],
131 }]
132 }
133 );
134
135 let output = PluginOutputMessages::OutputCells(vec![Cell {
136 character: 'f',
137 coordinates: (1, 2),
138 bg: None,
139 fg: Some((0.1, 0.2, 0.3, 0.4)),
140 }]);
141
142 assert_eq!(
143 expected.to_string(),
144 serde_json::to_string(&output).unwrap()
145 );
146 }
147
148 #[test]
149 fn output_pixels() {
150 let expected = serde_json::json!(
151 {
152 "output_pixels": [{
153 "coordinates": [1, 2],
154 "color": [0.1, 0.2, 0.3, 0.4],
155 }]
156 }
157 );
158
159 let output = PluginOutputMessages::OutputPixels(vec![Pixel {
160 coordinates: (1, 2),
161 color: Some((0.1, 0.2, 0.3, 0.4)),
162 }]);
163
164 assert_eq!(
165 expected.to_string(),
166 serde_json::to_string(&output).unwrap()
167 );
168 }
169
170 #[test]
171 fn input_pty_update() {
172 let expected = serde_json::json!(
173 {
174 "pty_update": {
175 "size": [1, 2],
176 "cells": [{
177 "character": "f",
178 "coordinates": [1, 2],
179 "bg": null,
180 "fg": [0.1, 0.2, 0.3, 0.4],
181 }],
182 "cursor": [9, 10],
183 }
184 }
185 );
186
187 let output = PluginInputMessages::PTYUpdate {
188 size: (1, 2),
189 cells: vec![Cell {
190 character: 'f',
191 coordinates: (1, 2),
192 bg: None,
193 fg: Some((0.1, 0.2, 0.3, 0.4)),
194 }],
195 cursor: (9, 10),
196 };
197
198 assert_eq!(
199 expected.to_string(),
200 serde_json::to_string(&output).unwrap()
201 );
202 }
203
204 #[test]
205 fn input_tty_resize() {
206 let expected = serde_json::json!(
207 {
208 "tty_resize": {
209 "width": 1,
210 "height": 2,
211 }
212 }
213 );
214
215 let output = PluginInputMessages::TTYResize {
216 width: 1,
217 height: 2,
218 };
219
220 assert_eq!(
221 expected.to_string(),
222 serde_json::to_string(&output).unwrap()
223 );
224 }
225}