feat: DOC-FULL #63 #64 #70 DocumentInfo + Position restore

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-07 20:31:59 +08:00
parent 6499e59718
commit 5fb372eb58
2 changed files with 105 additions and 0 deletions

View File

@ -9,7 +9,88 @@ pub struct DocumentInfo {
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 => (true, true, true),
MaterialType::Pdf => (true, true, true),
MaterialType::Image => (false, true, true),
MaterialType::Epub => (true, 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,
}
}
}
#[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);
}
}

View File

@ -130,6 +130,20 @@ impl ReadingPosition {
}
}
/// Check if a position type is compatible with a material type.
pub fn is_compatible_with(&self, mt: &crate::material_type::MaterialType) -> bool {
use crate::material_type::MaterialType;
matches!(
(self, mt),
(ReadingPosition::Markdown { .. }, MaterialType::Markdown)
| (ReadingPosition::Text { .. }, MaterialType::Text)
| (ReadingPosition::Pdf { .. }, MaterialType::Pdf)
| (ReadingPosition::Image { .. }, MaterialType::Image)
| (ReadingPosition::Epub { .. }, MaterialType::Epub)
| (ReadingPosition::Unknown, _)
)
}
pub fn progress_value(&self) -> Option<f32> {
match self {
Self::Markdown { scroll_progress, .. } => Some(clamp_progress(*scroll_progress)),
@ -186,6 +200,16 @@ mod tests {
assert!(json.contains("\"overallProgress\":1.0"));
}
#[test]
fn test_is_compatible() {
use crate::material_type::MaterialType;
let md = ReadingPosition::Markdown { block_id: "h1".into(), scroll_progress: 0.5 };
assert!(md.is_compatible_with(&MaterialType::Markdown));
assert!(!md.is_compatible_with(&MaterialType::Pdf));
assert!(ReadingPosition::Unknown.is_compatible_with(&MaterialType::Pdf));
assert!(ReadingPosition::Unknown.is_compatible_with(&MaterialType::Markdown)); // Unknown = compatible with all
}
#[test]
fn test_progress_value() {
assert_eq!(ReadingPosition::Unknown.progress_value(), None);