serializer.rs

3.2 kB · rust · 94 lines

1use super::models::Cell2d;2use crate::dim::serializer::{byte_grid, color_grid, count_grid, parse, tag_layer, types_field};3use mrlycore::errors::{value_error, MrlyError, Result};4use mrlycore::json;5use mrlycore::tensor::Tensor;67/// Returns the cell's types as rows of bytes.8pub fn to_lists(cell: &Cell2d) -> Vec<Vec<u8>> {9    let (h, w) = (cell.height(), cell.width());10    (0..h)11        .map(|y| (0..w).map(|x| cell.types().get(&[y, x])).collect())12        .collect()13}1415/// Builds a cell from rows of bytes, or an error when the rows are empty or ragged.16pub fn from_lists(lists: &[Vec<u8>]) -> Result<Cell2d> {17    if lists.is_empty() {18        return value_error("cannot build a cell from an empty list.");19    }20    let (h, w) = (lists.len(), lists[0].len());21    if lists.iter().any(|row| row.len() != w) {22        return value_error("all rows must have the same length.");23    }24    let data: Vec<u8> = lists.iter().flatten().copied().collect();25    Ok(Cell2d::new(Tensor::of(data, vec![h, w])))26}2728/// Returns the cell's types as rows of digit strings.29pub fn to_strings(cell: &Cell2d) -> Vec<String> {30    to_lists(cell)31        .iter()32        .map(|row| row.iter().map(|v| v.to_string()).collect())33        .collect()34}3536/// Builds a cell from rows of digit strings, or an error at any non-digit.37///38/// ```39/// let rows = vec!["111".to_string(), "101".to_string(), "111".to_string()];40/// let cell = mrlymath::two::from_strings(&rows).unwrap();41/// assert_eq!(mrlymath::two::to_lists(&cell)[1], vec![1, 0, 1]);42/// ```43pub fn from_strings(rows: &[String]) -> Result<Cell2d> {44    let lists: Result<Vec<Vec<u8>>> = rows45        .iter()46        .map(|row| {47            row.chars()48                .map(|ch| {49                    ch.to_digit(10)50                        .map(|d| d as u8)51                        .ok_or_else(|| MrlyError::Value(format!("invalid digit {ch:?}.")))52                })53                .collect()54        })55        .collect();56    from_lists(&lists?)57}5859/// Serializes the cell to a JSON string of its types, with colors and tags when present.60pub fn to_json(cell: &Cell2d) -> String {61    let mut data = json!({62        "v": 1,63        "width": cell.width(),64        "height": cell.height(),65        "types": to_lists(cell),66    });67    if let Some(colors) = &cell.cell.colors {68        data["colors"] = json!(colors.chunks(cell.width()).collect::<Vec<_>>());69    }70    if let Some(tags) = &cell.cell.tags {71        let (h, w) = (cell.height(), cell.width());72        let nested: Vec<Vec<i64>> = (0..h)73            .map(|r| (0..w).map(|c| tags.at(r * w + c)).collect())74            .collect();75        data["tags"] = json!(nested);76    }77    data.to_string()78}7980/// Restores a cell from its JSON string, colors and tags included, or a parse error.81pub fn from_json(text: &str) -> Result<Cell2d> {82    let data = parse(text)?;83    let lists = byte_grid(types_field(&data)?)?;84    let mut cell = from_lists(&lists)?;85    if let Some(colors) = data.get("colors") {86        let nested = color_grid(colors)?;87        cell.cell.colors = Some(nested.into_iter().flatten().collect());88    }89    if let Some(tags) = data.get("tags") {90        let shape = vec![cell.height(), cell.width()];91        cell.cell.tags = Some(tag_layer(&count_grid(tags)?, shape)?);92    }93    Ok(cell)94}