1use std::marker::PhantomData;
57
58use schemars::JsonSchema;
59use serde::{Deserialize, Serialize};
60
61use crate::error::CoreResult;
62use crate::metadata::Metadata;
63use crate::section::Section;
64use crate::validate::validate_sections;
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub struct Draft;
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct Validated;
85
86pub struct Document<S = Draft> {
113 sections: Vec<Section>,
114 metadata: Metadata,
115 _state: PhantomData<S>,
116}
117
118impl<S> Document<S> {
123 pub fn sections(&self) -> &[Section] {
134 &self.sections
135 }
136
137 pub fn metadata(&self) -> &Metadata {
139 &self.metadata
140 }
141
142 pub fn section_count(&self) -> usize {
144 self.sections.len()
145 }
146
147 pub fn is_empty(&self) -> bool {
149 self.sections.is_empty()
150 }
151}
152
153impl Document<Draft> {
158 pub fn new() -> Self {
169 Self { sections: Vec::new(), metadata: Metadata::default(), _state: PhantomData }
170 }
171
172 pub fn with_metadata(metadata: Metadata) -> Self {
187 Self { sections: Vec::new(), metadata, _state: PhantomData }
188 }
189
190 pub fn add_section(&mut self, section: Section) {
204 self.sections.push(section);
205 }
206
207 pub fn set_metadata(&mut self, metadata: Metadata) {
209 self.metadata = metadata;
210 }
211
212 pub fn metadata_mut(&mut self) -> &mut Metadata {
214 &mut self.metadata
215 }
216
217 pub fn sections_mut(&mut self) -> &mut [Section] {
219 &mut self.sections
220 }
221
222 pub fn validate(self) -> CoreResult<Document<Validated>> {
262 validate_sections(&self.sections)?;
263 Ok(Document { sections: self.sections, metadata: self.metadata, _state: PhantomData })
264 }
265}
266
267impl Default for Document<Draft> {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272
273impl<S> std::fmt::Debug for Document<S> {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 f.debug_struct("Document")
280 .field("sections", &self.sections)
281 .field("metadata", &self.metadata)
282 .finish()
283 }
284}
285
286impl<S> Clone for Document<S> {
287 fn clone(&self) -> Self {
288 Self {
289 sections: self.sections.clone(),
290 metadata: self.metadata.clone(),
291 _state: PhantomData,
292 }
293 }
294}
295
296impl<S> PartialEq for Document<S> {
297 fn eq(&self, other: &Self) -> bool {
298 self.sections == other.sections && self.metadata == other.metadata
299 }
300}
301
302impl<S> Eq for Document<S> {}
303
304impl<S> std::fmt::Display for Document<S> {
305 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306 write!(f, "Document({} sections)", self.sections.len())
307 }
308}
309
310impl<S> Serialize for Document<S> {
315 fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
316 use serde::ser::SerializeStruct;
317 let mut state = serializer.serialize_struct("Document", 2)?;
318 state.serialize_field("sections", &self.sections)?;
319 state.serialize_field("metadata", &self.metadata)?;
320 state.end()
321 }
322}
323
324impl<'de> Deserialize<'de> for Document<Draft> {
325 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
326 #[derive(Deserialize)]
327 struct DocumentData {
328 sections: Vec<Section>,
329 metadata: Metadata,
330 }
331
332 let data = DocumentData::deserialize(deserializer)?;
333 Ok(Document { sections: data.sections, metadata: data.metadata, _state: PhantomData })
334 }
335}
336
337impl<S> JsonSchema for Document<S> {
342 fn schema_name() -> std::borrow::Cow<'static, str> {
343 "Document".into()
344 }
345
346 fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
347 schemars::json_schema!({
348 "type": "object",
349 "properties": {
350 "sections": gen.subschema_for::<Vec<Section>>(),
351 "metadata": gen.subschema_for::<crate::metadata::Metadata>(),
352 },
353 "required": ["sections", "metadata"]
354 })
355 }
356}
357
358const _: () = {
363 #[allow(dead_code)]
364 fn assert_send<T: Send>() {}
365 #[allow(dead_code)]
366 fn assert_sync<T: Sync>() {}
367 #[allow(dead_code)]
368 fn verify() {
369 assert_send::<Document<Draft>>();
370 assert_sync::<Document<Draft>>();
371 assert_send::<Document<Validated>>();
372 assert_sync::<Document<Validated>>();
373 }
374};
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379 use crate::error::CoreError;
380 use crate::page::PageSettings;
381 use crate::paragraph::Paragraph;
382 use crate::run::Run;
383 use hwpforge_foundation::{CharShapeIndex, ParaShapeIndex};
384
385 fn valid_section() -> Section {
386 Section::with_paragraphs(
387 vec![Paragraph::with_runs(
388 vec![Run::text("Hello", CharShapeIndex::new(0))],
389 ParaShapeIndex::new(0),
390 )],
391 PageSettings::a4(),
392 )
393 }
394
395 #[test]
398 fn new_creates_empty_draft() {
399 let doc = Document::new();
400 assert!(doc.is_empty());
401 assert_eq!(doc.section_count(), 0);
402 assert!(doc.metadata().title.is_none());
403 }
404
405 #[test]
406 fn with_metadata() {
407 let meta = Metadata { title: Some("Test".to_string()), ..Metadata::default() };
408 let doc = Document::with_metadata(meta);
409 assert_eq!(doc.metadata().title.as_deref(), Some("Test"));
410 }
411
412 #[test]
413 fn default_is_new() {
414 let a = Document::new();
415 let b = Document::default();
416 assert_eq!(a, b);
417 }
418
419 #[test]
422 fn add_section() {
423 let mut doc = Document::new();
424 doc.add_section(valid_section());
425 assert_eq!(doc.section_count(), 1);
426 assert!(!doc.is_empty());
427 }
428
429 #[test]
430 fn add_multiple_sections() {
431 let mut doc = Document::new();
432 doc.add_section(valid_section());
433 doc.add_section(valid_section());
434 doc.add_section(valid_section());
435 assert_eq!(doc.section_count(), 3);
436 }
437
438 #[test]
439 fn set_metadata() {
440 let mut doc = Document::new();
441 doc.set_metadata(Metadata { title: Some("New".to_string()), ..Metadata::default() });
442 assert_eq!(doc.metadata().title.as_deref(), Some("New"));
443 }
444
445 #[test]
446 fn metadata_mut() {
447 let mut doc = Document::new();
448 doc.metadata_mut().title = Some("Mutated".to_string());
449 assert_eq!(doc.metadata().title.as_deref(), Some("Mutated"));
450 }
451
452 #[test]
453 fn sections_mut() {
454 let mut doc = Document::new();
455 doc.add_section(valid_section());
456 doc.add_section(valid_section());
457 assert_eq!(doc.sections_mut().len(), 2);
458 }
459
460 #[test]
463 fn validate_success() {
464 let mut doc = Document::new();
465 doc.add_section(valid_section());
466 let validated = doc.validate().unwrap();
467 assert_eq!(validated.section_count(), 1);
468 }
469
470 #[test]
471 fn validate_empty_document_fails() {
472 let doc = Document::new();
473 let err = doc.validate().unwrap_err();
474 assert!(matches!(err, CoreError::Validation(_)));
475 }
476
477 #[test]
478 fn validate_empty_section_fails() {
479 let mut doc = Document::new();
480 doc.add_section(Section::new(PageSettings::a4()));
481 assert!(doc.validate().is_err());
482 }
483
484 #[test]
485 fn validate_consumes_draft() {
486 let mut doc = Document::new();
487 doc.add_section(valid_section());
488 let _validated = doc.validate().unwrap();
489 }
491
492 #[test]
495 fn validated_has_read_methods() {
496 let mut doc = Document::new();
497 doc.add_section(valid_section());
498 let validated = doc.validate().unwrap();
499
500 assert_eq!(validated.section_count(), 1);
501 assert!(!validated.is_empty());
502 assert_eq!(validated.sections().len(), 1);
503 assert!(validated.metadata().title.is_none());
504 }
505
506 #[test]
509 fn display_draft() {
510 let doc = Document::new();
511 assert_eq!(doc.to_string(), "Document(0 sections)");
512 }
513
514 #[test]
515 fn display_validated() {
516 let mut doc = Document::new();
517 doc.add_section(valid_section());
518 let validated = doc.validate().unwrap();
519 assert_eq!(validated.to_string(), "Document(1 sections)");
520 }
521
522 #[test]
525 fn equality_draft() {
526 let mut a = Document::new();
527 a.add_section(valid_section());
528 let mut b = Document::new();
529 b.add_section(valid_section());
530 assert_eq!(a, b);
531 }
532
533 #[test]
534 fn equality_validated() {
535 let mut a = Document::new();
536 a.add_section(valid_section());
537 let mut b = Document::new();
538 b.add_section(valid_section());
539 let va = a.validate().unwrap();
540 let vb = b.validate().unwrap();
541 assert_eq!(va, vb);
542 }
543
544 #[test]
547 fn clone_draft() {
548 let mut doc = Document::new();
549 doc.add_section(valid_section());
550 let cloned = doc.clone();
551 assert_eq!(doc, cloned);
552 }
553
554 #[test]
555 fn clone_validated() {
556 let mut doc = Document::new();
557 doc.add_section(valid_section());
558 let validated = doc.validate().unwrap();
559 let cloned = validated.clone();
560 assert_eq!(validated, cloned);
561 }
562
563 #[test]
566 fn serde_roundtrip_draft() {
567 let mut doc = Document::new();
568 doc.add_section(valid_section());
569 doc.set_metadata(Metadata { title: Some("Test".to_string()), ..Metadata::default() });
570
571 let json = serde_json::to_string(&doc).unwrap();
572 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
573 assert_eq!(doc, back);
574 }
575
576 #[test]
577 fn serde_roundtrip_validated_deserializes_to_draft() {
578 let mut doc = Document::new();
579 doc.add_section(valid_section());
580 let validated = doc.validate().unwrap();
581
582 let json = serde_json::to_string(&validated).unwrap();
583 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
585 let re_validated = back.validate().unwrap();
587 assert_eq!(validated, re_validated);
588 }
589
590 #[test]
591 fn serde_empty_document() {
592 let doc = Document::new();
593 let json = serde_json::to_string(&doc).unwrap();
594 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
595 assert_eq!(doc, back);
596 }
597
598 #[test]
601 fn complex_document_roundtrip() {
602 use crate::control::Control;
603 use crate::image::{Image, ImageFormat};
604 use crate::table::{Table, TableCell, TableRow};
605 use hwpforge_foundation::HwpUnit;
606
607 let cell = TableCell::new(
608 vec![Paragraph::with_runs(
609 vec![Run::text("cell", CharShapeIndex::new(0))],
610 ParaShapeIndex::new(0),
611 )],
612 HwpUnit::from_mm(50.0).unwrap(),
613 );
614 let table = Table::new(vec![TableRow { cells: vec![cell], height: None }]);
615
616 let link = Control::Hyperlink {
617 text: "click".to_string(),
618 url: "https://example.com".to_string(),
619 };
620
621 let img = Image::new(
622 "test.png",
623 HwpUnit::from_mm(10.0).unwrap(),
624 HwpUnit::from_mm(10.0).unwrap(),
625 ImageFormat::Png,
626 );
627
628 let section = Section::with_paragraphs(
629 vec![
630 Paragraph::with_runs(
631 vec![
632 Run::text("Hello ", CharShapeIndex::new(0)),
633 Run::text("world", CharShapeIndex::new(1)),
634 ],
635 ParaShapeIndex::new(0),
636 ),
637 Paragraph::with_runs(
638 vec![Run::table(table, CharShapeIndex::new(0))],
639 ParaShapeIndex::new(1),
640 ),
641 Paragraph::with_runs(
642 vec![Run::control(link, CharShapeIndex::new(0))],
643 ParaShapeIndex::new(0),
644 ),
645 Paragraph::with_runs(
646 vec![Run::image(img, CharShapeIndex::new(0))],
647 ParaShapeIndex::new(0),
648 ),
649 ],
650 PageSettings::a4(),
651 );
652
653 let mut doc = Document::with_metadata(Metadata {
654 title: Some("Complex Doc".to_string()),
655 author: Some("Author".to_string()),
656 keywords: vec!["test".to_string()],
657 ..Metadata::default()
658 });
659 doc.add_section(section);
660
661 let validated = doc.validate().unwrap();
662 let json = serde_json::to_string_pretty(&validated).unwrap();
663 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
664 let re_validated = back.validate().unwrap();
665 assert_eq!(validated, re_validated);
666 }
667
668 #[test]
671 fn debug_output() {
672 let doc = Document::new();
673 let s = format!("{doc:?}");
674 assert!(s.contains("Document"), "debug: {s}");
675 assert!(s.contains("sections"), "debug: {s}");
676 }
677}