1use std::fmt;
2use std::num::ParseIntError;
3use std::str::FromStr;
4
5use derive_more::{Display, Error};
6use serde::{Deserialize, Serialize};
7
8#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10pub struct PtySize {
11 pub rows: u16,
13
14 pub cols: u16,
16
17 #[serde(default)]
19 pub pixel_width: u16,
20
21 #[serde(default)]
23 pub pixel_height: u16,
24}
25
26impl PtySize {
27 pub fn from_rows_and_cols(rows: u16, cols: u16) -> Self {
29 Self {
30 rows,
31 cols,
32 ..Default::default()
33 }
34 }
35}
36
37impl fmt::Display for PtySize {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(f, "{},{}", self.rows, self.cols)?;
43 if self.pixel_width > 0 || self.pixel_height > 0 {
44 write!(f, ",{},{}", self.pixel_width, self.pixel_height)?;
45 }
46
47 Ok(())
48 }
49}
50
51impl Default for PtySize {
52 fn default() -> Self {
53 PtySize {
54 rows: 24,
55 cols: 80,
56 pixel_width: 0,
57 pixel_height: 0,
58 }
59 }
60}
61
62#[derive(Clone, Debug, PartialEq, Eq, Display, Error)]
63pub enum PtySizeParseError {
64 MissingRows,
65 MissingColumns,
66 InvalidRows(ParseIntError),
67 InvalidColumns(ParseIntError),
68 InvalidPixelWidth(ParseIntError),
69 InvalidPixelHeight(ParseIntError),
70}
71
72impl FromStr for PtySize {
73 type Err = PtySizeParseError;
74
75 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 let mut tokens = s.split(',');
81
82 Ok(Self {
83 rows: tokens
84 .next()
85 .ok_or(PtySizeParseError::MissingRows)?
86 .trim()
87 .parse()
88 .map_err(PtySizeParseError::InvalidRows)?,
89 cols: tokens
90 .next()
91 .ok_or(PtySizeParseError::MissingColumns)?
92 .trim()
93 .parse()
94 .map_err(PtySizeParseError::InvalidColumns)?,
95 pixel_width: tokens
96 .next()
97 .map(|s| s.trim().parse())
98 .transpose()
99 .map_err(PtySizeParseError::InvalidPixelWidth)?
100 .unwrap_or(0),
101 pixel_height: tokens
102 .next()
103 .map(|s| s.trim().parse())
104 .transpose()
105 .map_err(PtySizeParseError::InvalidPixelHeight)?
106 .unwrap_or(0),
107 })
108 }
109}
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn should_be_able_to_serialize_to_json() {
116 let size = PtySize {
117 rows: 10,
118 cols: 20,
119 pixel_width: 30,
120 pixel_height: 40,
121 };
122
123 let value = serde_json::to_value(size).unwrap();
124 assert_eq!(
125 value,
126 serde_json::json!({
127 "rows": 10,
128 "cols": 20,
129 "pixel_width": 30,
130 "pixel_height": 40,
131 })
132 );
133 }
134
135 #[test]
136 fn should_be_able_to_deserialize_minimal_size_from_json() {
137 let value = serde_json::json!({
138 "rows": 10,
139 "cols": 20,
140 });
141
142 let size: PtySize = serde_json::from_value(value).unwrap();
143 assert_eq!(
144 size,
145 PtySize {
146 rows: 10,
147 cols: 20,
148 pixel_width: 0,
149 pixel_height: 0,
150 }
151 );
152 }
153
154 #[test]
155 fn should_be_able_to_deserialize_full_size_from_json() {
156 let value = serde_json::json!({
157 "rows": 10,
158 "cols": 20,
159 "pixel_width": 30,
160 "pixel_height": 40,
161 });
162
163 let size: PtySize = serde_json::from_value(value).unwrap();
164 assert_eq!(
165 size,
166 PtySize {
167 rows: 10,
168 cols: 20,
169 pixel_width: 30,
170 pixel_height: 40,
171 }
172 );
173 }
174
175 #[test]
176 fn should_be_able_to_serialize_to_msgpack() {
177 let size = PtySize {
178 rows: 10,
179 cols: 20,
180 pixel_width: 30,
181 pixel_height: 40,
182 };
183
184 let _ = rmp_serde::encode::to_vec_named(&size).unwrap();
189 }
190
191 #[test]
192 fn should_be_able_to_deserialize_minimal_size_from_msgpack() {
193 #[derive(Serialize)]
198 struct PartialSize {
199 rows: u16,
200 cols: u16,
201 }
202 let buf = rmp_serde::encode::to_vec_named(&PartialSize { rows: 10, cols: 20 }).unwrap();
203
204 let size: PtySize = rmp_serde::decode::from_slice(&buf).unwrap();
205 assert_eq!(
206 size,
207 PtySize {
208 rows: 10,
209 cols: 20,
210 pixel_width: 0,
211 pixel_height: 0,
212 }
213 );
214 }
215
216 #[test]
217 fn should_be_able_to_deserialize_full_size_from_msgpack() {
218 let buf = rmp_serde::encode::to_vec_named(&PtySize {
223 rows: 10,
224 cols: 20,
225 pixel_width: 30,
226 pixel_height: 40,
227 })
228 .unwrap();
229
230 let size: PtySize = rmp_serde::decode::from_slice(&buf).unwrap();
231 assert_eq!(
232 size,
233 PtySize {
234 rows: 10,
235 cols: 20,
236 pixel_width: 30,
237 pixel_height: 40,
238 }
239 );
240 }
241}