- Add 9 new tests: Text, Pdf, Image, Epub, Excel, PowerPoint, all_stats_start_none, file_size, material_type_matches - Total 12 tests covering all 9 MaterialType variants - Capabilities (is_searchable, supports_position, supports_anchor) + preview_mode verified per type Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
222 lines
7.5 KiB
Rust
222 lines
7.5 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::material_type::{MaterialType, PreviewMode};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
|
pub struct DocumentInfo {
|
|
pub material_id: String,
|
|
pub title: String,
|
|
pub material_type: MaterialType,
|
|
pub preview_mode: PreviewMode,
|
|
pub file_size: u64,
|
|
|
|
// Text stats
|
|
pub page_count: Option<u32>,
|
|
pub word_count: Option<u32>,
|
|
pub char_count: Option<u32>,
|
|
pub line_count: Option<u32>,
|
|
|
|
// Image
|
|
pub image_width: Option<u32>,
|
|
pub image_height: Option<u32>,
|
|
pub image_format: Option<String>,
|
|
|
|
// EPUB
|
|
pub epub_chapter_count: Option<u32>,
|
|
|
|
// Capabilities
|
|
pub is_searchable: bool,
|
|
pub supports_position: bool,
|
|
pub supports_anchor: bool,
|
|
|
|
pub created_at: Option<String>,
|
|
}
|
|
|
|
impl DocumentInfo {
|
|
pub fn new(material_id: String, title: String, material_type: MaterialType, file_size: u64) -> Self {
|
|
let preview_mode = material_type.preview_mode();
|
|
let (is_searchable, supports_position, supports_anchor) = match material_type {
|
|
MaterialType::Markdown | MaterialType::Text | MaterialType::Pdf | MaterialType::Epub => (true, true, true),
|
|
MaterialType::Image => (false, true, true),
|
|
MaterialType::Word | MaterialType::Excel | MaterialType::PowerPoint => (false, false, true),
|
|
MaterialType::Unknown => (false, false, false),
|
|
};
|
|
Self {
|
|
material_id,
|
|
title,
|
|
material_type,
|
|
preview_mode,
|
|
file_size,
|
|
page_count: None,
|
|
word_count: None,
|
|
char_count: None,
|
|
line_count: None,
|
|
image_width: None,
|
|
image_height: None,
|
|
image_format: None,
|
|
epub_chapter_count: None,
|
|
is_searchable,
|
|
supports_position,
|
|
supports_anchor,
|
|
created_at: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Build a fully populated DocumentInfo from a file path.
|
|
pub fn build_document_info(
|
|
file_path: &str,
|
|
material_id: String,
|
|
title: String,
|
|
) -> Result<DocumentInfo, crate::error::DocumentError> {
|
|
use std::path::Path;
|
|
use crate::material_type::detect_material_type;
|
|
|
|
let path = Path::new(file_path);
|
|
let mt = detect_material_type(file_path).unwrap_or(MaterialType::Unknown);
|
|
let file_size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
|
|
|
|
let mut info = DocumentInfo::new(material_id, title, mt, file_size);
|
|
|
|
match &info.material_type {
|
|
MaterialType::Markdown | MaterialType::Text => {
|
|
if let Ok(content) = std::fs::read_to_string(path) {
|
|
let stats = crate::text::text_stats(&content);
|
|
info.word_count = Some(stats.word_count);
|
|
info.line_count = Some(stats.line_count);
|
|
info.char_count = Some(content.chars().count() as u32);
|
|
info.page_count = Some((stats.line_count / 50).max(1));
|
|
}
|
|
}
|
|
MaterialType::Image => {
|
|
if let Ok(meta) = crate::image_meta::read_image_meta(file_path) {
|
|
info.image_width = Some(meta.width);
|
|
info.image_height = Some(meta.height);
|
|
info.image_format = Some(meta.format);
|
|
}
|
|
}
|
|
MaterialType::Pdf => {
|
|
if let Ok(meta) = crate::pdf::read_pdf_metadata(path) {
|
|
info.page_count = Some(meta.page_count);
|
|
}
|
|
}
|
|
MaterialType::Epub => {
|
|
if let Ok(meta) = crate::epub::read_epub_metadata(path) {
|
|
info.epub_chapter_count = Some(meta.chapter_count);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
Ok(info)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_markdown_capabilities() {
|
|
let info = DocumentInfo::new("m1".into(), "test.md".into(), MaterialType::Markdown, 1024);
|
|
assert!(info.is_searchable);
|
|
assert!(info.supports_position);
|
|
assert_eq!(info.preview_mode, PreviewMode::NativeReader);
|
|
}
|
|
|
|
#[test]
|
|
fn test_office_capabilities() {
|
|
let info = DocumentInfo::new("m2".into(), "report.docx".into(), MaterialType::Word, 2048);
|
|
assert!(!info.is_searchable);
|
|
assert!(!info.supports_position);
|
|
assert!(info.supports_anchor); // Material-level anchor only
|
|
assert_eq!(info.preview_mode, PreviewMode::PlatformPreview);
|
|
}
|
|
|
|
#[test]
|
|
fn test_unknown_capabilities() {
|
|
let info = DocumentInfo::new("m3".into(), "file.bin".into(), MaterialType::Unknown, 0);
|
|
assert!(!info.is_searchable);
|
|
assert!(!info.supports_position);
|
|
assert!(!info.supports_anchor);
|
|
}
|
|
|
|
#[test]
|
|
fn test_text_capabilities() {
|
|
let info = DocumentInfo::new("m4".into(), "notes.txt".into(), MaterialType::Text, 512);
|
|
assert!(info.is_searchable);
|
|
assert!(info.supports_position);
|
|
assert!(info.supports_anchor);
|
|
assert_eq!(info.preview_mode, PreviewMode::NativeReader);
|
|
}
|
|
|
|
#[test]
|
|
fn test_pdf_capabilities() {
|
|
let info = DocumentInfo::new("m5".into(), "book.pdf".into(), MaterialType::Pdf, 2048);
|
|
assert!(info.is_searchable);
|
|
assert!(info.supports_position);
|
|
assert!(info.supports_anchor);
|
|
assert_eq!(info.preview_mode, PreviewMode::PlatformPreview);
|
|
}
|
|
|
|
#[test]
|
|
fn test_image_capabilities() {
|
|
let info = DocumentInfo::new("m6".into(), "photo.jpg".into(), MaterialType::Image, 1024);
|
|
assert!(!info.is_searchable);
|
|
assert!(info.supports_position);
|
|
assert!(info.supports_anchor);
|
|
assert_eq!(info.preview_mode, PreviewMode::NativeReader);
|
|
}
|
|
|
|
#[test]
|
|
fn test_epub_capabilities() {
|
|
let info = DocumentInfo::new("m7".into(), "book.epub".into(), MaterialType::Epub, 4096);
|
|
assert!(info.is_searchable);
|
|
assert!(info.supports_position);
|
|
assert!(info.supports_anchor);
|
|
assert_eq!(info.preview_mode, PreviewMode::NativeReader);
|
|
}
|
|
|
|
#[test]
|
|
fn test_excel_capabilities() {
|
|
let info = DocumentInfo::new("m8".into(), "sheet.xlsx".into(), MaterialType::Excel, 2048);
|
|
assert!(!info.is_searchable);
|
|
assert!(!info.supports_position);
|
|
assert!(info.supports_anchor);
|
|
assert_eq!(info.preview_mode, PreviewMode::PlatformPreview);
|
|
}
|
|
|
|
#[test]
|
|
fn test_powerpoint_capabilities() {
|
|
let info = DocumentInfo::new("m9".into(), "slides.pptx".into(), MaterialType::PowerPoint, 4096);
|
|
assert!(!info.is_searchable);
|
|
assert!(!info.supports_position);
|
|
assert!(info.supports_anchor);
|
|
assert_eq!(info.preview_mode, PreviewMode::ExternalOpen);
|
|
}
|
|
|
|
#[test]
|
|
fn test_all_stats_fields_start_none() {
|
|
let info = DocumentInfo::new("m10".into(), "any".into(), MaterialType::Markdown, 100);
|
|
assert_eq!(info.page_count, None);
|
|
assert_eq!(info.word_count, None);
|
|
assert_eq!(info.char_count, None);
|
|
assert_eq!(info.line_count, None);
|
|
assert_eq!(info.image_width, None);
|
|
assert_eq!(info.image_height, None);
|
|
assert_eq!(info.image_format, None);
|
|
assert_eq!(info.epub_chapter_count, None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_file_size_is_set() {
|
|
let info = DocumentInfo::new("m11".into(), "f".into(), MaterialType::Markdown, 12345);
|
|
assert_eq!(info.file_size, 12345);
|
|
}
|
|
|
|
#[test]
|
|
fn test_material_type_matches_input() {
|
|
let info = DocumentInfo::new("m12".into(), "t".into(), MaterialType::Pdf, 0);
|
|
assert_eq!(info.material_type, MaterialType::Pdf);
|
|
}
|
|
}
|