affine_doc_loader 0.1.9

AFFiNE document parsing and rendering utilities.
Documentation
use std::collections::BTreeMap;

use super::{
  Bounds, CanvasProjectionV1, PROJECTION_VERSION, ParseError, ProjectionTarget, known_element_type, normalize_document,
};

pub fn project_canvas(doc_bin: Vec<u8>, doc_id: String, revision: String) -> Result<CanvasProjectionV1, ParseError> {
  let normalized = normalize_document(doc_bin, ProjectionTarget::Canvas)?;
  let mut counts = BTreeMap::new();
  for element in &normalized.elements {
    *counts
      .entry(known_element_type(&element.element_type).to_string())
      .or_insert(0) += 1;
  }
  let bounds = aggregate_bounds(
    normalized
      .blocks
      .iter()
      .filter_map(|block| block.bounds.as_ref())
      .chain(normalized.elements.iter().filter_map(|element| element.bounds.as_ref())),
  );
  Ok(CanvasProjectionV1 {
    version: PROJECTION_VERSION,
    doc_id,
    revision,
    title: normalized.title,
    surface_block_id: normalized.surface_block_id,
    bounds,
    counts,
    blocks: normalized.blocks,
    elements: normalized.elements,
    warnings: normalized.warnings,
  })
}

fn aggregate_bounds<'a>(bounds: impl Iterator<Item = &'a Bounds>) -> Option<Bounds> {
  bounds.fold(None, |aggregate, item| match aggregate {
    None => Some(item.clone()),
    Some(current) => {
      let x = current.x.min(item.x);
      let y = current.y.min(item.y);
      let right = (current.x + current.width).max(item.x + item.width);
      let bottom = (current.y + current.height).max(item.y + item.height);
      Some(Bounds {
        x,
        y,
        width: right - x,
        height: bottom - y,
      })
    }
  })
}