|
| 1 | +use crate::domain::models::{Document, FileEntry, NotesStructure}; |
| 2 | +use crate::domain::parsing::MarkdownParser; |
| 3 | +use crate::domain::services::{FileService, FileServiceError}; |
| 4 | +use crate::infrastructure::RealFileService; |
| 5 | +use std::path::{Path, PathBuf}; |
| 6 | +use std::sync::Arc; |
| 7 | + |
| 8 | +#[derive(Debug, thiserror::Error)] |
| 9 | +pub enum DocumentServiceError { |
| 10 | + #[error("File service error: {0}")] |
| 11 | + FileService(#[from] FileServiceError), |
| 12 | + #[error("Parse error: {0}")] |
| 13 | + Parse(String), |
| 14 | +} |
| 15 | + |
| 16 | +#[derive(Clone)] |
| 17 | +pub struct DocumentService { |
| 18 | + file_service: Arc<dyn FileService>, |
| 19 | + parser: Arc<dyn MarkdownParser>, |
| 20 | +} |
| 21 | + |
| 22 | +impl PartialEq for DocumentService { |
| 23 | + fn eq(&self, other: &Self) -> bool { |
| 24 | + Arc::ptr_eq(&self.file_service, &other.file_service) |
| 25 | + && Arc::ptr_eq(&self.parser, &other.parser) |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +impl DocumentService { |
| 30 | + pub fn new(file_service: Arc<dyn FileService>, parser: Arc<dyn MarkdownParser>) -> Self { |
| 31 | + Self { |
| 32 | + file_service, |
| 33 | + parser, |
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + pub fn load_document(&self, path: &Path) -> Result<Document, DocumentServiceError> { |
| 38 | + let content = self.file_service.read_file(path)?; |
| 39 | + let document = self.parser.parse(&content, path.to_path_buf()); |
| 40 | + Ok(document) |
| 41 | + } |
| 42 | + |
| 43 | + pub fn scan_markdown_files(&self, root: &Path) -> Result<Vec<FileEntry>, DocumentServiceError> { |
| 44 | + self.file_service |
| 45 | + .scan_markdown_files(root) |
| 46 | + .map_err(DocumentServiceError::FileService) |
| 47 | + } |
| 48 | + |
| 49 | + pub fn validate_notes_structure( |
| 50 | + &self, |
| 51 | + root: &Path, |
| 52 | + ) -> Result<NotesStructure, DocumentServiceError> { |
| 53 | + self.file_service |
| 54 | + .validate_notes_structure(root) |
| 55 | + .map_err(DocumentServiceError::FileService) |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +#[derive(Clone, PartialEq)] |
| 60 | +pub struct ApplicationServices { |
| 61 | + pub document_service: DocumentService, |
| 62 | +} |
| 63 | + |
| 64 | +impl ApplicationServices { |
| 65 | + pub fn new() -> Self { |
| 66 | + let file_service = Arc::new(RealFileService::new()); |
| 67 | + let parser = Arc::new(crate::domain::parsing::PulldownMarkdownParser::new()); |
| 68 | + let document_service = DocumentService::new(file_service, parser); |
| 69 | + |
| 70 | + Self { document_service } |
| 71 | + } |
| 72 | + |
| 73 | + #[cfg(test)] |
| 74 | + pub fn with_mock_file_service(file_service: Arc<dyn FileService>) -> Self { |
| 75 | + let parser = Arc::new(crate::domain::parsing::PulldownMarkdownParser::new()); |
| 76 | + let document_service = DocumentService::new(file_service, parser); |
| 77 | + |
| 78 | + Self { document_service } |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +impl Default for ApplicationServices { |
| 83 | + fn default() -> Self { |
| 84 | + Self::new() |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +#[cfg(test)] |
| 89 | +mod tests { |
| 90 | + use super::*; |
| 91 | + use crate::infrastructure::MockFileService; |
| 92 | + |
| 93 | + #[test] |
| 94 | + fn test_document_service_with_mock() { |
| 95 | + let mut mock_fs = MockFileService::new(); |
| 96 | + mock_fs.add_file("/test.md", "- Item 1\n- Item 2"); |
| 97 | + |
| 98 | + let services = ApplicationServices::with_mock_file_service(Arc::new(mock_fs)); |
| 99 | + let doc = services |
| 100 | + .document_service |
| 101 | + .load_document(Path::new("/test.md")) |
| 102 | + .unwrap(); |
| 103 | + |
| 104 | + assert_eq!(doc.outline.len(), 2); |
| 105 | + assert_eq!(doc.outline[0].content, "Item 1"); |
| 106 | + assert_eq!(doc.outline[1].content, "Item 2"); |
| 107 | + } |
| 108 | + |
| 109 | + #[test] |
| 110 | + fn test_scan_files_with_mock() { |
| 111 | + let mut mock_fs = MockFileService::new(); |
| 112 | + mock_fs.add_file("/notes/test1.md", "# Test 1"); |
| 113 | + mock_fs.add_file("/notes/test2.md", "# Test 2"); |
| 114 | + |
| 115 | + let services = ApplicationServices::with_mock_file_service(Arc::new(mock_fs)); |
| 116 | + let files = services |
| 117 | + .document_service |
| 118 | + .scan_markdown_files(Path::new("/notes")) |
| 119 | + .unwrap(); |
| 120 | + |
| 121 | + assert_eq!(files.len(), 2); |
| 122 | + } |
| 123 | +} |
0 commit comments