use serde::de::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{Cell, Screen, TermState};
#[derive(Serialize, Deserialize)]
struct Cursor {
row: u16,
col: u16,
visible: bool,
}
const JSON_FORMAT: u32 = 1;
#[derive(Serialize)]
struct Wire<'a> {
format: u32,
cols: u16,
rows: u16,
cursor: Cursor,
cells: Vec<&'a [Cell]>,
state: &'a TermState,
}
fn format_when_absent() -> u32 {
JSON_FORMAT
}
#[derive(Deserialize)]
struct OwnedWire {
#[serde(default = "format_when_absent")]
format: u32,
cols: u16,
rows: u16,
cursor: Cursor,
cells: Vec<Vec<Cell>>,
state: TermState,
}
impl Serialize for Screen {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let width = usize::from(self.cols).max(1);
Wire {
format: JSON_FORMAT,
cols: self.cols,
rows: self.rows,
cursor: Cursor {
row: self.cursor_row,
col: self.cursor_col,
visible: self.cursor_visible,
},
cells: self.cells.chunks(width).collect(),
state: &self.state,
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Screen {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let wire = OwnedWire::deserialize(deserializer)?;
if wire.format != JSON_FORMAT {
return Err(D::Error::custom(format!(
"saved-screen JSON format {} is not one this termlens reads (it reads format {JSON_FORMAT}); \
a newer termlens wrote this file",
wire.format
)));
}
if wire.cells.len() != usize::from(wire.rows) {
return Err(D::Error::custom(format!(
"a {}x{} screen needs {} rows of cells, not {}",
wire.cols,
wire.rows,
wire.rows,
wire.cells.len()
)));
}
if let Some((row, cells)) = wire
.cells
.iter()
.enumerate()
.find(|(_, row)| row.len() != usize::from(wire.cols))
{
return Err(D::Error::custom(format!(
"row {row} of a {}-column screen holds {} cells",
wire.cols,
cells.len()
)));
}
if wire.cursor.row >= wire.rows.max(1) || wire.cursor.col > wire.cols {
return Err(D::Error::custom(format!(
"cursor {},{} is outside a {}x{} screen",
wire.cursor.row, wire.cursor.col, wire.cols, wire.rows
)));
}
Ok(Screen::from_parts(
wire.cols,
wire.rows,
wire.cursor.row,
wire.cursor.col,
wire.cursor.visible,
wire.cells.into_iter().flatten().collect(),
wire.state,
))
}
}