affine_doc_loader 0.1.9

AFFiNE document parsing and rendering utilities.
Documentation
use super::{
  builder::{insert_text, text_ops_from_plain},
  root_doc::ensure_pages_array,
  *,
};

pub fn update_doc_title(existing_binary: &[u8], doc_id: &str, title: &str) -> Result<Vec<u8>, ParseError> {
  let doc = load_doc(existing_binary, Some(doc_id))?;

  let state_before = doc.get_state_vector();
  let blocks_map = doc.get_map("blocks")?;
  if blocks_map.is_empty() {
    return Err(ParseError::ParserError("blocks map is empty".into()));
  }

  let mut page_block = find_page_block(&blocks_map)?;
  let current = get_string(&page_block, PROP_TITLE).unwrap_or_default();
  if current != title {
    insert_text(&doc, &mut page_block, PROP_TITLE, &text_ops_from_plain(title))?;
  }

  Ok(doc.encode_state_as_update_v1(&state_before)?)
}

pub fn update_root_doc_meta_title(root_doc_bin: &[u8], doc_id: &str, title: &str) -> Result<Vec<u8>, ParseError> {
  let doc = load_doc_or_new(root_doc_bin)?;

  let state_before = doc.get_state_vector();
  let mut meta = doc.get_or_create_map("meta")?;
  let mut pages = ensure_pages_array(&doc, &mut meta)?;

  let mut found = false;
  for idx in 0..pages.len() {
    let Some(mut page) = pages.get(idx).and_then(|v| v.to_map()) else {
      continue;
    };
    if get_string(&page, "id").as_deref() == Some(doc_id) {
      page.insert("title".to_string(), Any::String(title.to_string()))?;
      found = true;
      break;
    }
  }

  if !found {
    let page_map = doc.create_map()?;

    let idx = pages.len();
    pages.insert(idx, Value::Map(page_map))?;

    if let Some(mut inserted_page) = pages.get(idx).and_then(|v| v.to_map()) {
      inserted_page.insert("id".to_string(), Any::String(doc_id.to_string()))?;
      inserted_page.insert("title".to_string(), Any::String(title.to_string()))?;

      let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_millis() as i64)
        .unwrap_or(0);
      inserted_page.insert("createDate".to_string(), Any::Float64((timestamp as f64).into()))?;

      let tags = doc.create_array()?;
      inserted_page.insert("tags".to_string(), Value::Array(tags))?;
    }
  }

  Ok(doc.encode_state_as_update_v1(&state_before)?)
}

fn find_page_block(blocks_map: &Map) -> Result<Map, ParseError> {
  let index = build_block_index(blocks_map);
  let page_id = find_block_id_by_flavour(&index.block_pool, PAGE_FLAVOUR)
    .ok_or_else(|| ParseError::ParserError("page block not found".into()))?;
  blocks_map
    .get(&page_id)
    .and_then(|value| value.to_map())
    .ok_or_else(|| ParseError::ParserError("page block not found".into()))
}

#[cfg(test)]
#[path = "../tests/write/doc_meta.rs"]
mod tests;