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> {
110 sections: Vec<Section>,
111 metadata: Metadata,
112 _state: PhantomData<S>,
113}
114
115impl<S> Document<S> {
120 pub fn sections(&self) -> &[Section] {
131 &self.sections
132 }
133
134 pub fn metadata(&self) -> &Metadata {
136 &self.metadata
137 }
138
139 pub fn section_count(&self) -> usize {
141 self.sections.len()
142 }
143
144 pub fn is_empty(&self) -> bool {
146 self.sections.is_empty()
147 }
148}
149
150impl Document<Draft> {
155 pub fn new() -> Self {
166 Self { sections: Vec::new(), metadata: Metadata::default(), _state: PhantomData }
167 }
168
169 pub fn with_metadata(metadata: Metadata) -> Self {
181 Self { sections: Vec::new(), metadata, _state: PhantomData }
182 }
183
184 pub fn add_section(&mut self, section: Section) {
198 self.sections.push(section);
199 }
200
201 pub fn set_metadata(&mut self, metadata: Metadata) {
203 self.metadata = metadata;
204 }
205
206 pub fn metadata_mut(&mut self) -> &mut Metadata {
208 &mut self.metadata
209 }
210
211 pub fn sections_mut(&mut self) -> &mut [Section] {
213 &mut self.sections
214 }
215
216 pub fn validate(self) -> CoreResult<Document<Validated>> {
256 validate_sections(&self.sections)?;
257 Ok(Document { sections: self.sections, metadata: self.metadata, _state: PhantomData })
258 }
259}
260
261impl Default for Document<Draft> {
262 fn default() -> Self {
263 Self::new()
264 }
265}
266
267impl<S> std::fmt::Debug for Document<S> {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 f.debug_struct("Document")
274 .field("sections", &self.sections)
275 .field("metadata", &self.metadata)
276 .finish()
277 }
278}
279
280impl<S> Clone for Document<S> {
281 fn clone(&self) -> Self {
282 Self {
283 sections: self.sections.clone(),
284 metadata: self.metadata.clone(),
285 _state: PhantomData,
286 }
287 }
288}
289
290impl<S> PartialEq for Document<S> {
291 fn eq(&self, other: &Self) -> bool {
292 self.sections == other.sections && self.metadata == other.metadata
293 }
294}
295
296impl<S> Eq for Document<S> {}
297
298impl<S> std::fmt::Display for Document<S> {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 write!(f, "Document({} sections)", self.sections.len())
301 }
302}
303
304impl<S> Serialize for Document<S> {
309 fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
310 use serde::ser::SerializeStruct;
311 let mut state = serializer.serialize_struct("Document", 2)?;
312 state.serialize_field("sections", &self.sections)?;
313 state.serialize_field("metadata", &self.metadata)?;
314 state.end()
315 }
316}
317
318impl<'de> Deserialize<'de> for Document<Draft> {
319 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
320 #[derive(Deserialize)]
321 struct DocumentData {
322 sections: Vec<Section>,
323 metadata: Metadata,
324 }
325
326 let data = DocumentData::deserialize(deserializer)?;
327 Ok(Document { sections: data.sections, metadata: data.metadata, _state: PhantomData })
328 }
329}
330
331impl<S> JsonSchema for Document<S> {
336 fn schema_name() -> std::borrow::Cow<'static, str> {
337 "Document".into()
338 }
339
340 fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
341 schemars::json_schema!({
342 "type": "object",
343 "properties": {
344 "sections": gen.subschema_for::<Vec<Section>>(),
345 "metadata": gen.subschema_for::<crate::metadata::Metadata>(),
346 },
347 "required": ["sections", "metadata"]
348 })
349 }
350}
351
352const _: () = {
357 #[allow(dead_code)]
358 fn assert_send<T: Send>() {}
359 #[allow(dead_code)]
360 fn assert_sync<T: Sync>() {}
361 #[allow(dead_code)]
362 fn verify() {
363 assert_send::<Document<Draft>>();
364 assert_sync::<Document<Draft>>();
365 assert_send::<Document<Validated>>();
366 assert_sync::<Document<Validated>>();
367 }
368};
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373 use crate::error::CoreError;
374 use crate::page::PageSettings;
375 use crate::paragraph::Paragraph;
376 use crate::run::Run;
377 use hwpforge_foundation::{CharShapeIndex, ParaShapeIndex};
378
379 fn valid_section() -> Section {
380 Section::with_paragraphs(
381 vec![Paragraph::with_runs(
382 vec![Run::text("Hello", CharShapeIndex::new(0))],
383 ParaShapeIndex::new(0),
384 )],
385 PageSettings::a4(),
386 )
387 }
388
389 #[test]
392 fn new_creates_empty_draft() {
393 let doc = Document::new();
394 assert!(doc.is_empty());
395 assert_eq!(doc.section_count(), 0);
396 assert!(doc.metadata().title.is_none());
397 }
398
399 #[test]
400 fn with_metadata() {
401 let meta = Metadata { title: Some("Test".to_string()), ..Metadata::default() };
402 let doc = Document::with_metadata(meta);
403 assert_eq!(doc.metadata().title.as_deref(), Some("Test"));
404 }
405
406 #[test]
407 fn default_is_new() {
408 let a = Document::new();
409 let b = Document::default();
410 assert_eq!(a, b);
411 }
412
413 #[test]
416 fn add_section() {
417 let mut doc = Document::new();
418 doc.add_section(valid_section());
419 assert_eq!(doc.section_count(), 1);
420 assert!(!doc.is_empty());
421 }
422
423 #[test]
424 fn add_multiple_sections() {
425 let mut doc = Document::new();
426 doc.add_section(valid_section());
427 doc.add_section(valid_section());
428 doc.add_section(valid_section());
429 assert_eq!(doc.section_count(), 3);
430 }
431
432 #[test]
433 fn set_metadata() {
434 let mut doc = Document::new();
435 doc.set_metadata(Metadata { title: Some("New".to_string()), ..Metadata::default() });
436 assert_eq!(doc.metadata().title.as_deref(), Some("New"));
437 }
438
439 #[test]
440 fn metadata_mut() {
441 let mut doc = Document::new();
442 doc.metadata_mut().title = Some("Mutated".to_string());
443 assert_eq!(doc.metadata().title.as_deref(), Some("Mutated"));
444 }
445
446 #[test]
447 fn sections_mut() {
448 let mut doc = Document::new();
449 doc.add_section(valid_section());
450 doc.add_section(valid_section());
451 assert_eq!(doc.sections_mut().len(), 2);
452 }
453
454 #[test]
457 fn validate_success() {
458 let mut doc = Document::new();
459 doc.add_section(valid_section());
460 let validated = doc.validate().unwrap();
461 assert_eq!(validated.section_count(), 1);
462 }
463
464 #[test]
465 fn validate_empty_document_fails() {
466 let doc = Document::new();
467 let err = doc.validate().unwrap_err();
468 assert!(matches!(err, CoreError::Validation(_)));
469 }
470
471 #[test]
472 fn validate_empty_section_fails() {
473 let mut doc = Document::new();
474 doc.add_section(Section::new(PageSettings::a4()));
475 assert!(doc.validate().is_err());
476 }
477
478 #[test]
479 fn validate_consumes_draft() {
480 let mut doc = Document::new();
481 doc.add_section(valid_section());
482 let _validated = doc.validate().unwrap();
483 }
485
486 #[test]
489 fn validated_has_read_methods() {
490 let mut doc = Document::new();
491 doc.add_section(valid_section());
492 let validated = doc.validate().unwrap();
493
494 assert_eq!(validated.section_count(), 1);
495 assert!(!validated.is_empty());
496 assert_eq!(validated.sections().len(), 1);
497 assert!(validated.metadata().title.is_none());
498 }
499
500 #[test]
503 fn display_draft() {
504 let doc = Document::new();
505 assert_eq!(doc.to_string(), "Document(0 sections)");
506 }
507
508 #[test]
509 fn display_validated() {
510 let mut doc = Document::new();
511 doc.add_section(valid_section());
512 let validated = doc.validate().unwrap();
513 assert_eq!(validated.to_string(), "Document(1 sections)");
514 }
515
516 #[test]
519 fn equality_draft() {
520 let mut a = Document::new();
521 a.add_section(valid_section());
522 let mut b = Document::new();
523 b.add_section(valid_section());
524 assert_eq!(a, b);
525 }
526
527 #[test]
528 fn equality_validated() {
529 let mut a = Document::new();
530 a.add_section(valid_section());
531 let mut b = Document::new();
532 b.add_section(valid_section());
533 let va = a.validate().unwrap();
534 let vb = b.validate().unwrap();
535 assert_eq!(va, vb);
536 }
537
538 #[test]
541 fn clone_draft() {
542 let mut doc = Document::new();
543 doc.add_section(valid_section());
544 let cloned = doc.clone();
545 assert_eq!(doc, cloned);
546 }
547
548 #[test]
549 fn clone_validated() {
550 let mut doc = Document::new();
551 doc.add_section(valid_section());
552 let validated = doc.validate().unwrap();
553 let cloned = validated.clone();
554 assert_eq!(validated, cloned);
555 }
556
557 #[test]
560 fn serde_roundtrip_draft() {
561 let mut doc = Document::new();
562 doc.add_section(valid_section());
563 doc.set_metadata(Metadata { title: Some("Test".to_string()), ..Metadata::default() });
564
565 let json = serde_json::to_string(&doc).unwrap();
566 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
567 assert_eq!(doc, back);
568 }
569
570 #[test]
571 fn serde_roundtrip_validated_deserializes_to_draft() {
572 let mut doc = Document::new();
573 doc.add_section(valid_section());
574 let validated = doc.validate().unwrap();
575
576 let json = serde_json::to_string(&validated).unwrap();
577 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
579 let re_validated = back.validate().unwrap();
581 assert_eq!(validated, re_validated);
582 }
583
584 #[test]
585 fn serde_empty_document() {
586 let doc = Document::new();
587 let json = serde_json::to_string(&doc).unwrap();
588 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
589 assert_eq!(doc, back);
590 }
591
592 #[test]
595 fn complex_document_roundtrip() {
596 use crate::control::Control;
597 use crate::image::{Image, ImageFormat};
598 use crate::table::{Table, TableCell, TableRow};
599 use hwpforge_foundation::HwpUnit;
600
601 let cell = TableCell::new(
602 vec![Paragraph::with_runs(
603 vec![Run::text("cell", CharShapeIndex::new(0))],
604 ParaShapeIndex::new(0),
605 )],
606 HwpUnit::from_mm(50.0).unwrap(),
607 );
608 let table = Table::new(vec![TableRow::new(vec![cell])]);
609
610 let link = Control::Hyperlink {
611 text: "click".to_string(),
612 url: "https://example.com".to_string(),
613 };
614
615 let img = Image::new(
616 "test.png",
617 HwpUnit::from_mm(10.0).unwrap(),
618 HwpUnit::from_mm(10.0).unwrap(),
619 ImageFormat::Png,
620 );
621
622 let section = Section::with_paragraphs(
623 vec![
624 Paragraph::with_runs(
625 vec![
626 Run::text("Hello ", CharShapeIndex::new(0)),
627 Run::text("world", CharShapeIndex::new(1)),
628 ],
629 ParaShapeIndex::new(0),
630 ),
631 Paragraph::with_runs(
632 vec![Run::table(table, CharShapeIndex::new(0))],
633 ParaShapeIndex::new(1),
634 ),
635 Paragraph::with_runs(
636 vec![Run::control(link, CharShapeIndex::new(0))],
637 ParaShapeIndex::new(0),
638 ),
639 Paragraph::with_runs(
640 vec![Run::image(img, CharShapeIndex::new(0))],
641 ParaShapeIndex::new(0),
642 ),
643 ],
644 PageSettings::a4(),
645 );
646
647 let mut doc = Document::with_metadata(Metadata {
648 title: Some("Complex Doc".to_string()),
649 author: Some("Author".to_string()),
650 keywords: vec!["test".to_string()],
651 ..Metadata::default()
652 });
653 doc.add_section(section);
654
655 let validated = doc.validate().unwrap();
656 let json = serde_json::to_string_pretty(&validated).unwrap();
657 let back: Document<Draft> = serde_json::from_str(&json).unwrap();
658 let re_validated = back.validate().unwrap();
659 assert_eq!(validated, re_validated);
660 }
661
662 #[test]
665 fn debug_output() {
666 let doc = Document::new();
667 let s = format!("{doc:?}");
668 assert!(s.contains("Document"), "debug: {s}");
669 assert!(s.contains("sections"), "debug: {s}");
670 }
671}