text-document-io 1.5.0

Import/export for text-document: plain text, Markdown, HTML, LaTeX, DOCX
Documentation
// Generated by Qleany v1.4.8 from feature_use_case.tera
use crate::ExportPlainTextDto;
use anyhow::{Result, anyhow};
use common::database::QueryUnitOfWork;
use common::database::rope_helpers::{block_content_via_store, rope_flat_text_if_simple};
use common::entities::{Block, Document, Frame, Root};
use common::types::{EntityId, ROOT_ENTITY_ID};

pub trait ExportPlainTextUnitOfWorkFactoryTrait: Send + Sync {
    fn create(&self) -> Box<dyn ExportPlainTextUnitOfWorkTrait>;
}

#[macros::uow_action(entity = "Root", action = "GetRO")]
#[macros::uow_action(entity = "Root", action = "GetRelationshipRO")]
#[macros::uow_action(entity = "Document", action = "GetRO")]
#[macros::uow_action(entity = "Document", action = "GetRelationshipRO")]
#[macros::uow_action(entity = "Frame", action = "GetRO")]
#[macros::uow_action(entity = "Frame", action = "GetRelationshipRO")]
#[macros::uow_action(entity = "Block", action = "GetMultiRO")]
pub trait ExportPlainTextUnitOfWorkTrait: QueryUnitOfWork {}

pub struct ExportPlainTextUseCase {
    uow_factory: Box<dyn ExportPlainTextUnitOfWorkFactoryTrait>,
}

impl ExportPlainTextUseCase {
    pub fn new(uow_factory: Box<dyn ExportPlainTextUnitOfWorkFactoryTrait>) -> Self {
        ExportPlainTextUseCase { uow_factory }
    }

    pub fn execute(&mut self) -> Result<ExportPlainTextDto> {
        let uow = self.uow_factory.create();
        uow.begin_transaction()?;

        // Step 1: Get Root (id=1) and its Document via relationship
        let root = uow
            .get_root(&ROOT_ENTITY_ID)?
            .ok_or_else(|| anyhow!("Root entity not found"))?;

        let doc_ids = uow.get_root_relationship(
            &root.id,
            &common::direct_access::root::RootRelationshipField::Document,
        )?;
        let doc_id = *doc_ids
            .first()
            .ok_or_else(|| anyhow!("Root has no associated Document"))?;

        // Step 2: Get all Frame IDs from Document.Frames relationship
        let frame_ids = uow.get_document_relationship(
            &doc_id,
            &common::direct_access::document::DocumentRelationshipField::Frames,
        )?;

        let store = uow.store();

        // Fast path: flat single-frame document with no tables has its
        // entire plain-text representation already laid out in rope
        // byte order. One allocation replaces the per-block walk.
        if let Some(plain_text) = rope_flat_text_if_simple(&store, frame_ids.len()) {
            uow.end_transaction()?;
            return Ok(ExportPlainTextDto { plain_text });
        }

        // Slow path: tables or multi-frame documents require the
        // per-frame, per-block traversal because cell content lives in
        // separate byte ranges later in the rope (plan ยง1.6).
        let mut all_plain_texts: Vec<String> = Vec::new();

        for frame_id in &frame_ids {
            // Get Block IDs from the Frame.Blocks relationship
            let block_ids = uow.get_frame_relationship(
                frame_id,
                &common::direct_access::frame::FrameRelationshipField::Blocks,
            )?;

            if block_ids.is_empty() {
                continue;
            }

            // Get all blocks in batch and sort by document_position
            let blocks_opt = uow.get_block_multi(&block_ids)?;
            let mut blocks: Vec<Block> = blocks_opt.into_iter().flatten().collect();
            blocks.sort_by_key(|b| b.document_position);

            for block in &blocks {
                all_plain_texts.push(block_content_via_store(block, &store));
            }
        }

        let plain_text = all_plain_texts.join("\n");

        uow.end_transaction()?;

        Ok(ExportPlainTextDto { plain_text })
    }
}