serializer.rs

4.1 kB · rust · 119 lines

1use super::Cell3d;2use crate::core::errors::{value_error, Result};3use crate::core::tensor::Tensor;4use crate::core::{json, Json};5use crate::math::dim::serializer::{byte_cube, count_cube, parse, tag_layer, types_field};6use serde::Deserialize;78/// Unrolls the cell into nested lists, plane by row by site.9fn to_lists(cell: &Cell3d) -> Vec<Vec<Vec<u8>>> {10    let shape = &cell.types().shape;11    (0..shape[0])12        .map(|i| {13            (0..shape[1])14                .map(|j| {15                    (0..shape[2])16                        .map(|k| cell.types().get(&[i, j, k]))17                        .collect()18                })19                .collect()20        })21        .collect()22}2324/// Builds a cell from nested lists, or an error when they are empty or ragged.25fn from_lists(lists: &[Vec<Vec<u8>>]) -> Result<Cell3d> {26    if lists.is_empty() || lists[0].is_empty() || lists[0][0].is_empty() {27        return value_error("cannot build a cell from an empty list.");28    }29    let (a, b, c) = (lists.len(), lists[0].len(), lists[0][0].len());30    for plane in lists {31        if plane.len() != b || plane.iter().any(|row| row.len() != c) {32            return value_error("all planes and rows must have the same lengths.");33        }34    }35    let data: Vec<u8> = lists.iter().flatten().flatten().copied().collect();36    Ok(Cell3d::new(Tensor::of(data, vec![a, b, c])))37}3839fn color_cube(value: &Json) -> Result<Vec<Vec<Vec<[u8; 4]>>>> {40    Ok(Vec::deserialize(value)?)41}4243fn planes<T: Clone>(flat: &[T], shape: &[usize]) -> Vec<Vec<Vec<T>>> {44    flat.chunks(shape[1] * shape[2])45        .map(|plane| plane.chunks(shape[2]).map(<[T]>::to_vec).collect())46        .collect()47}4849/// Serializes the cell's shape and types to JSON, with colors and tags when present.50pub fn to_json(cell: &Cell3d) -> String {51    let shape = &cell.types().shape;52    let mut data = json!({53        "v": 1,54        "height": shape[0],55        "width": shape[1],56        "depth": shape[2],57        "types": to_lists(cell),58    });59    if let Some(colors) = &cell.cell.colors {60        data["colors"] = json!(planes(colors, shape));61    }62    if let Some(tags) = &cell.cell.tags {63        let flat: Vec<i64> = (0..tags.size()).map(|at| tags.at(at)).collect();64        data["tags"] = json!(planes(&flat, shape));65    }66    data.to_string()67}6869/// Parses a cell from its JSON, colors and tags included, or a parse error.70pub fn from_json(text: &str) -> Result<Cell3d> {71    let data = parse(text)?;72    let lists = byte_cube(types_field(&data)?)?;73    let mut cell = from_lists(&lists)?;74    if let Some(colors) = data.get("colors") {75        let nested = color_cube(colors)?;76        cell.cell.colors = Some(nested.into_iter().flatten().flatten().collect());77    }78    if let Some(tags) = data.get("tags") {79        let shape = cell.types().shape.clone();80        cell.cell.tags = Some(tag_layer(&count_cube(tags)?, shape)?);81    }82    Ok(cell)83}8485#[cfg(test)]86mod tests {87    use super::*;88    use crate::core::cell::mapping;89    use crate::core::enums::Mode;90    use crate::math::three::designs;91    #[test]92    fn json_round_trip_with_colors_and_tags() {93        let c = designs::carpet(3, 1)94            .unwrap()95            .layers()96            .paint(&mapping(), Mode::Type);97        let restored = from_json(&to_json(&c)).unwrap();98        assert_eq!(c, restored);99        assert!(restored.cell.colors.is_some());100        assert!(restored.cell.tags.is_some());101    }102    #[test]103    fn json_round_trip_with_tags_past_a_byte() {104        use crate::core::tensor::Dtype;105        use crate::math::three::manhattan_layers;106        let long = manhattan_layers(Cell3d::new(Tensor::full(vec![1, 1, 600], 1)));107        let tags = long.cell.tags.as_ref().unwrap();108        assert_eq!(tags.dtype(), Dtype::U16);109        assert_eq!(tags.at(0), 299);110        let restored = from_json(&to_json(&long)).unwrap();111        assert_eq!(restored, long);112        assert_eq!(restored.cell.tags.as_ref().unwrap().at(0), 299);113    }114    #[test]115    fn lists_round_trip() {116        let c = designs::void(4, 1).unwrap();117        assert_eq!(from_lists(&to_lists(&c)).unwrap(), c);118    }119}