1use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11use crate::document::DocumentTree;
12use crate::parser::DocumentFormat;
13
14#[derive(Debug, Clone)]
20pub struct IndexedDocument {
21 pub id: String,
23
24 pub format: DocumentFormat,
26
27 pub name: String,
29
30 pub description: Option<String>,
32
33 pub source_path: Option<PathBuf>,
35
36 pub page_count: Option<usize>,
38
39 pub line_count: Option<usize>,
41
42 pub tree: Option<DocumentTree>,
44
45 pub pages: Vec<PageContent>,
47}
48
49impl IndexedDocument {
50 pub fn new(id: impl Into<String>, format: DocumentFormat) -> Self {
52 Self {
53 id: id.into(),
54 format,
55 name: String::new(),
56 description: None,
57 source_path: None,
58 page_count: None,
59 line_count: None,
60 tree: None,
61 pages: Vec::new(),
62 }
63 }
64
65 pub fn with_name(mut self, name: impl Into<String>) -> Self {
67 self.name = name.into();
68 self
69 }
70
71 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
73 self.description = Some(desc.into());
74 self
75 }
76
77 pub fn with_source_path(mut self, path: impl Into<PathBuf>) -> Self {
79 self.source_path = Some(path.into());
80 self
81 }
82
83 pub fn with_page_count(mut self, count: usize) -> Self {
85 self.page_count = Some(count);
86 self
87 }
88
89 pub fn with_line_count(mut self, count: usize) -> Self {
91 self.line_count = Some(count);
92 self
93 }
94
95 pub fn with_tree(mut self, tree: DocumentTree) -> Self {
97 self.tree = Some(tree);
98 self
99 }
100
101 pub fn add_page(&mut self, page: usize, content: impl Into<String>) {
103 self.pages.push(PageContent {
104 page,
105 content: content.into(),
106 });
107 }
108
109 pub fn is_loaded(&self) -> bool {
111 self.tree.is_some()
112 }
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct PageContent {
118 pub page: usize,
120
121 pub content: String,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
133pub enum IndexMode {
134 #[default]
139 Default,
140
141 Force,
146
147 Incremental,
152}
153
154#[derive(Debug, Clone)]
156pub struct IndexOptions {
157 pub mode: IndexMode,
159
160 pub generate_summaries: bool,
162
163 pub include_text: bool,
165
166 pub generate_ids: bool,
168
169 pub generate_description: bool,
171}
172
173impl Default for IndexOptions {
174 fn default() -> Self {
175 Self {
176 mode: IndexMode::Default,
177 generate_summaries: false,
178 include_text: true,
179 generate_ids: true,
180 generate_description: false,
181 }
182 }
183}
184
185impl IndexOptions {
186 pub fn new() -> Self {
188 Self::default()
189 }
190
191 pub fn with_summaries(mut self) -> Self {
193 self.generate_summaries = true;
194 self
195 }
196
197 pub fn with_description(mut self) -> Self {
199 self.generate_description = true;
200 self
201 }
202
203 pub fn with_mode(mut self, mode: IndexMode) -> Self {
211 self.mode = mode;
212 self
213 }
214}
215
216#[derive(Debug, Clone)]
222pub struct QueryResult {
223 pub doc_id: String,
225
226 pub node_ids: Vec<String>,
228
229 pub content: String,
231
232 pub score: f32,
234}
235
236impl QueryResult {
237 pub fn new(doc_id: impl Into<String>) -> Self {
239 Self {
240 doc_id: doc_id.into(),
241 node_ids: Vec::new(),
242 content: String::new(),
243 score: 0.0,
244 }
245 }
246
247 pub fn is_empty(&self) -> bool {
249 self.node_ids.is_empty()
250 }
251
252 pub fn len(&self) -> usize {
254 self.node_ids.len()
255 }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct DocumentInfo {
265 pub id: String,
267
268 pub name: String,
270
271 pub format: String,
273
274 pub description: Option<String>,
276
277 pub page_count: Option<usize>,
279
280 pub line_count: Option<usize>,
282}
283
284impl DocumentInfo {
285 pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
287 Self {
288 id: id.into(),
289 name: name.into(),
290 format: String::new(),
291 description: None,
292 page_count: None,
293 line_count: None,
294 }
295 }
296
297 pub fn with_format(mut self, format: impl Into<String>) -> Self {
299 self.format = format.into();
300 self
301 }
302}
303
304#[derive(Debug, Clone, thiserror::Error)]
310pub enum ClientError {
311 #[error("Document not found: {0}")]
313 NotFound(String),
314
315 #[error("Invalid operation: {0}")]
317 InvalidOperation(String),
318
319 #[error("Configuration error: {0}")]
321 Config(String),
322
323 #[error("Operation timed out")]
325 Timeout,
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 #[test]
333 fn test_indexed_document() {
334 let doc = IndexedDocument::new("doc-1", DocumentFormat::Markdown)
335 .with_name("Test Document")
336 .with_description("A test document");
337
338 assert_eq!(doc.id, "doc-1");
339 assert_eq!(doc.name, "Test Document");
340 assert!(doc.tree.is_none());
341 }
342
343 #[test]
344 fn test_index_options() {
345 let options = IndexOptions::new()
346 .with_summaries()
347 .with_mode(IndexMode::Force);
348
349 assert!(options.generate_summaries);
350 assert_eq!(options.mode, IndexMode::Force);
351 }
352
353 #[test]
354 fn test_query_result() {
355 let result = QueryResult::new("doc-1");
356 assert!(result.is_empty());
357 assert_eq!(result.len(), 0);
358 }
359
360 #[test]
361 fn test_document_info() {
362 let info = DocumentInfo::new("doc-1", "Test").with_format("markdown");
363
364 assert_eq!(info.id, "doc-1");
365 assert_eq!(info.format, "markdown");
366 }
367}