use crate::ImportPlainTextDto;
use anyhow::{Result, anyhow};
use common::database::CommandUnitOfWork;
use common::entities::{Block, Document, Frame, InlineContent, InlineElement, Root};
use common::types::{EntityId, ROOT_ENTITY_ID};
pub trait ImportPlainTextUnitOfWorkFactoryTrait: Send + Sync {
fn create(&self) -> Box<dyn ImportPlainTextUnitOfWorkTrait>;
}
#[macros::uow_action(entity = "Root", action = "Get")]
#[macros::uow_action(entity = "Root", action = "GetRelationship")]
#[macros::uow_action(entity = "Document", action = "Get")]
#[macros::uow_action(entity = "Document", action = "Update")]
#[macros::uow_action(entity = "Document", action = "GetRelationship")]
#[macros::uow_action(entity = "Frame", action = "Get")]
#[macros::uow_action(entity = "Frame", action = "Create")]
#[macros::uow_action(entity = "Frame", action = "Update")]
#[macros::uow_action(entity = "Frame", action = "Remove")]
#[macros::uow_action(entity = "Frame", action = "GetRelationship")]
#[macros::uow_action(entity = "Block", action = "Create")]
#[macros::uow_action(entity = "Block", action = "CreateMulti")]
#[macros::uow_action(entity = "InlineElement", action = "Create")]
#[macros::uow_action(entity = "InlineElement", action = "CreateMulti")]
pub trait ImportPlainTextUnitOfWorkTrait: CommandUnitOfWork {}
pub struct ImportPlainTextUseCase {
uow_factory: Box<dyn ImportPlainTextUnitOfWorkFactoryTrait>,
}
impl ImportPlainTextUseCase {
pub fn new(uow_factory: Box<dyn ImportPlainTextUnitOfWorkFactoryTrait>) -> Self {
ImportPlainTextUseCase { uow_factory }
}
pub fn execute(&mut self, dto: &ImportPlainTextDto) -> Result<()> {
let mut uow = self.uow_factory.create();
uow.begin_transaction()?;
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"))?;
let frame_ids = uow.get_document_relationship(
&doc_id,
&common::direct_access::document::DocumentRelationshipField::Frames,
)?;
for frame_id in &frame_ids {
uow.remove_frame(frame_id)?;
}
let new_frame = Frame::default();
let created_frame = uow.create_frame(&new_frame, doc_id, -1)?;
let normalized = dto.plain_text.replace("\r\n", "\n").replace('\r', "\n");
let lines: Vec<&str> = normalized.split('\n').collect();
let num_blocks = lines.len() as i64;
let mut total_chars: i64 = 0;
let mut document_position: i64 = 0;
let mut block_ids: Vec<i64> = Vec::new();
for (i, line) in lines.iter().enumerate() {
let line_len = line.chars().count() as i64;
let block = Block {
plain_text: line.to_string(),
text_length: line_len,
document_position,
..Block::default()
};
let created_block = uow.create_block(&block, created_frame.id, -1)?;
let element = InlineElement {
content: InlineContent::Text(line.to_string()),
..InlineElement::default()
};
uow.create_inline_element(&element, created_block.id, -1)?;
block_ids.push(created_block.id as i64);
total_chars += line_len;
document_position += line_len;
if i < lines.len() - 1 {
document_position += 1; }
}
let mut updated_frame = uow
.get_frame(&created_frame.id)?
.ok_or_else(|| anyhow!("Created frame not found"))?;
updated_frame.child_order = block_ids;
uow.update_frame(&updated_frame)?;
let mut updated_doc = uow
.get_document(&doc_id)?
.ok_or_else(|| anyhow!("Document not found after import"))?;
updated_doc.character_count = total_chars;
updated_doc.block_count = num_blocks;
uow.update_document(&updated_doc)?;
uow.commit()?;
Ok(())
}
}