1use thiserror::Error;
9
10use crate::{Span, parser::SourceLine};
11
12#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct Warning<'src> {
18 pub source: Span<'src>,
20
21 pub warning: WarningType,
23
24 pub origin: Option<SourceLine>,
44}
45
46#[derive(Clone, Eq, Error, PartialEq)]
51#[non_exhaustive]
52pub enum WarningType {
53 #[error("an attribute value is missing its terminating quote")]
56 AttributeValueMissingTerminatingQuote,
57
58 #[error(
61 "document header wasn't terminated by a blank line (this line can't be parsed as part of a document header)"
62 )]
63 DocumentHeaderNotTerminated,
64
65 #[error(
68 "no inline candidate; use the inline doctype to convert a single paragraph, verbatim, or raw block"
69 )]
70 NoInlineDoctypeCandidate,
71
72 #[error("an empty attribute value was detected")]
74 EmptyAttributeValue,
75
76 #[error(
79 "a shorthand element attribute marker ('.', '#', or '%') was found with no subsequent text"
80 )]
81 EmptyShorthandName,
82
83 #[error("macro name is not a valid identifier")]
85 InvalidMacroName,
86
87 #[error("media macro missing target")]
90 MediaMacroMissingTarget,
91
92 #[error("macro missing attribute list")]
94 MacroMissingAttributeList,
95
96 #[error("macro missing :: separator")]
99 MacroMissingSeparator,
100
101 #[error("missing comma after quoted attribute value")]
104 MissingCommaAfterQuotedAttributeValue,
105
106 #[error("closing marker for delimited block not found")]
109 UnterminatedDelimitedBlock,
110
111 #[error("a block title or attribute list was found without a subsequent block")]
115 MissingBlockAfterTitleOrAttributeList,
116
117 #[error("block anchor name is empty")]
119 EmptyBlockAnchorName,
120
121 #[error("block anchor name contains invalid name characters")]
124 InvalidBlockAnchorName,
125
126 #[error("attribute {0:?} can not be modified by document")]
129 AttributeValueIsLocked(String),
130
131 #[error("duplicate ID: {0:?} is already registered")]
134 DuplicateId(String),
135
136 #[error("level 0 section headings not supported")]
139 Level0SectionHeadingNotSupported,
140
141 #[error("section heading level skipped (expected {0}, found {1})")]
144 SectionHeadingLevelSkipped(usize, usize),
145
146 #[error("section heading level exceeds maximum (maximum 5, found {0})")]
149 SectionHeadingLevelExceedsMaximum(usize),
150
151 #[error("section heading level {0} is outside the supported range 1-5; clamped to {1}")]
155 SectionHeadingLevelOutOfRange(i32, usize),
156
157 #[error("leveloffset {0} places every section heading outside the supported range 1-5")]
160 LeveloffsetExcludesAllHeadingLevels(i32),
161
162 #[error("list item index: expected {0}, got {1}")]
165 ListItemOutOfSequence(String, String),
166
167 #[error("no callout found for <{0}>")]
170 NoCalloutFound(usize),
171
172 #[error("callout list item index: expected {0}, got {1}")]
175 CalloutListItemOutOfSequence(usize, usize),
176
177 #[error("dropping table cell because it exceeds the specified number of columns")]
180 TableCellExceedsColumnCount,
181
182 #[error("unclosed quote in CSV data; setting cell to empty")]
185 TableCsvDataHasUnclosedQuote,
186
187 #[error("table is missing a leading separator; recovering automatically")]
190 TableMissingLeadingSeparator,
191
192 #[error("dropping cells from incomplete row; detected end of table")]
195 TableIncompleteRowAtEndOfTable,
196
197 #[error("skipping reference to missing attribute: {0}")]
200 SkippingReferenceToMissingAttribute(String),
201
202 #[error("invalid substitution type for stem macro: {0}")]
205 InvalidSubstitutionTypeForStemMacro(String),
206
207 #[error("invalid substitution type for passthrough macro: {0}")]
210 InvalidSubstitutionTypeForPassthroughMacro(String),
211
212 #[error("invalid substitution type for block: {0}")]
216 InvalidSubstitutionTypeForBlock(String),
217
218 #[error("invalid footnote reference: {0}")]
221 InvalidFootnoteReference(String),
222
223 #[error("found deprecated footnoteref macro: {0}; use footnote macro with target instead")]
226 DeprecatedFootnoterefMacro(String),
227
228 #[error("include file not found: {0}")]
231 IncludeFileNotFound(String),
232
233 #[error("include dropped due to missing attribute: {0}")]
238 IncludeDroppedDueToMissingAttribute(String),
239
240 #[error("maximum include depth of {0} exceeded")]
247 MaxIncludeDepthExceeded(usize),
248
249 #[error("include encoding is not supported (only UTF-8 is supported): {0}")]
253 NonUtf8IncludeEncoding(String),
254
255 #[error("malformed preprocessor directive - {0}: {1}")]
261 MalformedConditionalDirective(String, String),
262
263 #[error("unmatched preprocessor directive: {0}")]
266 UnmatchedConditionalDirective(String),
267
268 #[error("mismatched preprocessor directive: {0}")]
272 MismatchedConditionalDirective(String),
273
274 #[error("detected unterminated preprocessor conditional directive: {0}")]
278 UnterminatedConditionalDirective(String),
279
280 #[error("{0} not found in include file")]
286 IncludeTagNotFound(String),
287
288 #[error("detected unclosed tag in include file: {0}")]
291 IncludeTagUnclosed(String),
292
293 #[error("mismatched end tag in include file (expected {0} but found {1})")]
297 IncludeTagMismatchedEnd(String, String),
298
299 #[error("unexpected end tag in include file: {0}")]
302 IncludeTagUnexpectedEnd(String),
303
304 #[error(
308 "abstract block cannot be used in a document without a doctitle when doctype is book. Excluding block content."
309 )]
310 AbstractBlockInBookWithoutDoctitle,
311
312 #[error("possible invalid reference: {0}")]
321 PossibleInvalidReference(String),
322}
323
324impl std::fmt::Debug for WarningType {
325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 match self {
327 WarningType::AttributeValueMissingTerminatingQuote => {
328 write!(f, "WarningType::AttributeValueMissingTerminatingQuote")
329 }
330
331 WarningType::DocumentHeaderNotTerminated => {
332 write!(f, "WarningType::DocumentHeaderNotTerminated")
333 }
334
335 WarningType::NoInlineDoctypeCandidate => {
336 write!(f, "WarningType::NoInlineDoctypeCandidate")
337 }
338
339 WarningType::EmptyAttributeValue => write!(f, "WarningType::EmptyAttributeValue"),
340 WarningType::EmptyShorthandName => write!(f, "WarningType::EmptyShorthandName"),
341 WarningType::InvalidMacroName => write!(f, "WarningType::InvalidMacroName"),
342
343 WarningType::MediaMacroMissingTarget => {
344 write!(f, "WarningType::MediaMacroMissingTarget")
345 }
346
347 WarningType::MacroMissingAttributeList => {
348 write!(f, "WarningType::MacroMissingAttributeList")
349 }
350
351 WarningType::MacroMissingSeparator => {
352 write!(f, "WarningType::MacroMissingSeparator")
353 }
354
355 WarningType::MissingCommaAfterQuotedAttributeValue => {
356 write!(f, "WarningType::MissingCommaAfterQuotedAttributeValue")
357 }
358
359 WarningType::UnterminatedDelimitedBlock => {
360 write!(f, "WarningType::UnterminatedDelimitedBlock")
361 }
362
363 WarningType::MissingBlockAfterTitleOrAttributeList => {
364 write!(f, "WarningType::MissingBlockAfterTitleOrAttributeList")
365 }
366
367 WarningType::EmptyBlockAnchorName => write!(f, "WarningType::EmptyBlockAnchorName"),
368 WarningType::InvalidBlockAnchorName => write!(f, "WarningType::InvalidBlockAnchorName"),
369
370 WarningType::AttributeValueIsLocked(value) => f
371 .debug_tuple("WarningType::AttributeValueIsLocked")
372 .field(value)
373 .finish(),
374
375 WarningType::DuplicateId(id) => {
376 f.debug_tuple("WarningType::DuplicateId").field(id).finish()
377 }
378
379 WarningType::Level0SectionHeadingNotSupported => {
380 write!(f, "WarningType::Level0SectionHeadingNotSupported")
381 }
382
383 WarningType::SectionHeadingLevelSkipped(expected, found) => f
384 .debug_tuple("WarningType::SectionHeadingLevelSkipped")
385 .field(expected)
386 .field(found)
387 .finish(),
388
389 WarningType::SectionHeadingLevelExceedsMaximum(found) => f
390 .debug_tuple("WarningType::SectionHeadingLevelExceedsMaximum")
391 .field(found)
392 .finish(),
393
394 WarningType::SectionHeadingLevelOutOfRange(computed, clamped) => f
395 .debug_tuple("WarningType::SectionHeadingLevelOutOfRange")
396 .field(computed)
397 .field(clamped)
398 .finish(),
399
400 WarningType::LeveloffsetExcludesAllHeadingLevels(offset) => f
401 .debug_tuple("WarningType::LeveloffsetExcludesAllHeadingLevels")
402 .field(offset)
403 .finish(),
404
405 WarningType::ListItemOutOfSequence(expected, actual) => f
406 .debug_tuple("WarningType::ListItemOutOfSequence")
407 .field(expected)
408 .field(actual)
409 .finish(),
410
411 WarningType::NoCalloutFound(number) => f
412 .debug_tuple("WarningType::NoCalloutFound")
413 .field(number)
414 .finish(),
415
416 WarningType::CalloutListItemOutOfSequence(expected, actual) => f
417 .debug_tuple("WarningType::CalloutListItemOutOfSequence")
418 .field(expected)
419 .field(actual)
420 .finish(),
421
422 WarningType::TableCellExceedsColumnCount => {
423 write!(f, "WarningType::TableCellExceedsColumnCount")
424 }
425
426 WarningType::TableCsvDataHasUnclosedQuote => {
427 write!(f, "WarningType::TableCsvDataHasUnclosedQuote")
428 }
429
430 WarningType::TableMissingLeadingSeparator => {
431 write!(f, "WarningType::TableMissingLeadingSeparator")
432 }
433
434 WarningType::TableIncompleteRowAtEndOfTable => {
435 write!(f, "WarningType::TableIncompleteRowAtEndOfTable")
436 }
437
438 WarningType::SkippingReferenceToMissingAttribute(name) => f
439 .debug_tuple("WarningType::SkippingReferenceToMissingAttribute")
440 .field(name)
441 .finish(),
442
443 WarningType::InvalidSubstitutionTypeForStemMacro(subs) => f
444 .debug_tuple("WarningType::InvalidSubstitutionTypeForStemMacro")
445 .field(subs)
446 .finish(),
447
448 WarningType::InvalidSubstitutionTypeForPassthroughMacro(subs) => f
449 .debug_tuple("WarningType::InvalidSubstitutionTypeForPassthroughMacro")
450 .field(subs)
451 .finish(),
452
453 WarningType::InvalidSubstitutionTypeForBlock(subs) => f
454 .debug_tuple("WarningType::InvalidSubstitutionTypeForBlock")
455 .field(subs)
456 .finish(),
457
458 WarningType::InvalidFootnoteReference(id) => f
459 .debug_tuple("WarningType::InvalidFootnoteReference")
460 .field(id)
461 .finish(),
462
463 WarningType::DeprecatedFootnoterefMacro(macro_text) => f
464 .debug_tuple("WarningType::DeprecatedFootnoterefMacro")
465 .field(macro_text)
466 .finish(),
467
468 WarningType::IncludeFileNotFound(target) => f
469 .debug_tuple("WarningType::IncludeFileNotFound")
470 .field(target)
471 .finish(),
472
473 WarningType::IncludeDroppedDueToMissingAttribute(directive) => f
474 .debug_tuple("WarningType::IncludeDroppedDueToMissingAttribute")
475 .field(directive)
476 .finish(),
477
478 WarningType::MaxIncludeDepthExceeded(depth) => f
479 .debug_tuple("WarningType::MaxIncludeDepthExceeded")
480 .field(depth)
481 .finish(),
482
483 WarningType::NonUtf8IncludeEncoding(encoding) => f
484 .debug_tuple("WarningType::NonUtf8IncludeEncoding")
485 .field(encoding)
486 .finish(),
487
488 WarningType::MalformedConditionalDirective(reason, directive) => f
489 .debug_tuple("WarningType::MalformedConditionalDirective")
490 .field(reason)
491 .field(directive)
492 .finish(),
493
494 WarningType::UnmatchedConditionalDirective(directive) => f
495 .debug_tuple("WarningType::UnmatchedConditionalDirective")
496 .field(directive)
497 .finish(),
498
499 WarningType::MismatchedConditionalDirective(directive) => f
500 .debug_tuple("WarningType::MismatchedConditionalDirective")
501 .field(directive)
502 .finish(),
503
504 WarningType::UnterminatedConditionalDirective(directive) => f
505 .debug_tuple("WarningType::UnterminatedConditionalDirective")
506 .field(directive)
507 .finish(),
508
509 WarningType::IncludeTagNotFound(tag) => f
510 .debug_tuple("WarningType::IncludeTagNotFound")
511 .field(tag)
512 .finish(),
513
514 WarningType::IncludeTagUnclosed(tag) => f
515 .debug_tuple("WarningType::IncludeTagUnclosed")
516 .field(tag)
517 .finish(),
518
519 WarningType::IncludeTagMismatchedEnd(expected, found) => f
520 .debug_tuple("WarningType::IncludeTagMismatchedEnd")
521 .field(expected)
522 .field(found)
523 .finish(),
524
525 WarningType::IncludeTagUnexpectedEnd(tag) => f
526 .debug_tuple("WarningType::IncludeTagUnexpectedEnd")
527 .field(tag)
528 .finish(),
529
530 WarningType::AbstractBlockInBookWithoutDoctitle => {
531 write!(f, "WarningType::AbstractBlockInBookWithoutDoctitle")
532 }
533
534 WarningType::PossibleInvalidReference(target) => f
535 .debug_tuple("WarningType::PossibleInvalidReference")
536 .field(target)
537 .finish(),
538 }
539 }
540}
541
542#[derive(Clone, Debug, Eq, PartialEq)]
544pub(crate) struct MatchAndWarnings<'src, T> {
545 pub(crate) item: T,
548
549 pub(crate) warnings: Vec<Warning<'src>>,
551}
552
553impl<T> MatchAndWarnings<'_, T> {
554 #[cfg(test)]
555 #[inline(always)]
556 #[track_caller]
557 #[allow(clippy::panic)] pub(crate) fn unwrap_if_no_warnings(self) -> T {
559 if self.warnings.is_empty() {
560 self.item
561 } else {
562 panic!(
563 "expected self.warnings to be empty\n\nfound warnings = {warnings:#?}\n",
564 warnings = self.warnings
565 );
566 }
567 }
568}
569
570#[cfg(test)]
571mod tests {
572 #![allow(clippy::unwrap_used)]
573
574 mod warning {
575 use crate::warnings::{Warning, WarningType};
576
577 #[test]
578 fn impl_clone() {
579 let w1 = Warning {
581 source: crate::Span::new("abc"),
582 warning: WarningType::EmptyAttributeValue,
583 origin: None,
584 };
585
586 let w2 = w1.clone();
587 assert_eq!(w1, w2);
588 }
589 }
590
591 mod warning_type {
592 mod impl_debug {
593 use crate::warnings::WarningType;
594
595 #[test]
596 fn attribute_value_missing_terminating_quote() {
597 let warning = WarningType::AttributeValueMissingTerminatingQuote;
598 let debug_output = format!("{:?}", warning);
599 assert_eq!(
600 debug_output,
601 "WarningType::AttributeValueMissingTerminatingQuote"
602 );
603 }
604
605 #[test]
606 fn document_header_not_terminated() {
607 let warning = WarningType::DocumentHeaderNotTerminated;
608 let debug_output = format!("{:?}", warning);
609 assert_eq!(debug_output, "WarningType::DocumentHeaderNotTerminated");
610 }
611
612 #[test]
613 fn no_inline_doctype_candidate() {
614 let warning = WarningType::NoInlineDoctypeCandidate;
615 let debug_output = format!("{:?}", warning);
616 assert_eq!(debug_output, "WarningType::NoInlineDoctypeCandidate");
617 }
618
619 #[test]
620 fn empty_attribute_value() {
621 let warning = WarningType::EmptyAttributeValue;
622 let debug_output = format!("{:?}", warning);
623 assert_eq!(debug_output, "WarningType::EmptyAttributeValue");
624 }
625
626 #[test]
627 fn empty_shorthand_name() {
628 let warning = WarningType::EmptyShorthandName;
629 let debug_output = format!("{:?}", warning);
630 assert_eq!(debug_output, "WarningType::EmptyShorthandName");
631 }
632
633 #[test]
634 fn invalid_macro_name() {
635 let warning = WarningType::InvalidMacroName;
636 let debug_output = format!("{:?}", warning);
637 assert_eq!(debug_output, "WarningType::InvalidMacroName");
638 }
639
640 #[test]
641 fn media_macro_missing_target() {
642 let warning = WarningType::MediaMacroMissingTarget;
643 let debug_output = format!("{:?}", warning);
644 assert_eq!(debug_output, "WarningType::MediaMacroMissingTarget");
645 }
646
647 #[test]
648 fn macro_missing_attribute_list() {
649 let warning = WarningType::MacroMissingAttributeList;
650 let debug_output = format!("{:?}", warning);
651 assert_eq!(debug_output, "WarningType::MacroMissingAttributeList");
652 }
653
654 #[test]
655 fn macro_missing_separator() {
656 let warning = WarningType::MacroMissingSeparator;
657 let debug_output = format!("{:?}", warning);
658 assert_eq!(debug_output, "WarningType::MacroMissingSeparator");
659 }
660
661 #[test]
662 fn missing_comma_after_quoted_attribute_value() {
663 let warning = WarningType::MissingCommaAfterQuotedAttributeValue;
664 let debug_output = format!("{:?}", warning);
665 assert_eq!(
666 debug_output,
667 "WarningType::MissingCommaAfterQuotedAttributeValue"
668 );
669 }
670
671 #[test]
672 fn unterminated_delimited_block() {
673 let warning = WarningType::UnterminatedDelimitedBlock;
674 let debug_output = format!("{:?}", warning);
675 assert_eq!(debug_output, "WarningType::UnterminatedDelimitedBlock");
676 }
677
678 #[test]
679 fn missing_block_after_title_or_attribute_list() {
680 let warning = WarningType::MissingBlockAfterTitleOrAttributeList;
681 let debug_output = format!("{:?}", warning);
682 assert_eq!(
683 debug_output,
684 "WarningType::MissingBlockAfterTitleOrAttributeList"
685 );
686 }
687
688 #[test]
689 fn empty_block_anchor_name() {
690 let warning = WarningType::EmptyBlockAnchorName;
691 let debug_output = format!("{:?}", warning);
692 assert_eq!(debug_output, "WarningType::EmptyBlockAnchorName");
693 }
694
695 #[test]
696 fn invalid_block_anchor_name() {
697 let warning = WarningType::InvalidBlockAnchorName;
698 let debug_output = format!("{:?}", warning);
699 assert_eq!(debug_output, "WarningType::InvalidBlockAnchorName");
700 }
701
702 #[test]
703 fn attribute_value_is_locked_simple_string() {
704 let warning = WarningType::AttributeValueIsLocked("test-attribute".to_string());
705 let debug_output = format!("{:?}", warning);
706 assert_eq!(
707 debug_output,
708 "WarningType::AttributeValueIsLocked(\"test-attribute\")"
709 );
710 }
711
712 #[test]
713 fn attribute_value_is_locked_empty_string() {
714 let warning = WarningType::AttributeValueIsLocked("".to_string());
715 let debug_output = format!("{:?}", warning);
716 assert_eq!(debug_output, "WarningType::AttributeValueIsLocked(\"\")");
717 }
718
719 #[test]
720 fn attribute_value_is_locked_string_with_special_chars() {
721 let warning =
722 WarningType::AttributeValueIsLocked("attr-with-special!@#$%^&*()".to_string());
723 let debug_output = format!("{:?}", warning);
724 assert_eq!(
725 debug_output,
726 "WarningType::AttributeValueIsLocked(\"attr-with-special!@#$%^&*()\")"
727 );
728 }
729
730 #[test]
731 fn attribute_value_is_locked_string_with_quotes() {
732 let warning = WarningType::AttributeValueIsLocked("attr\"with'quotes".to_string());
733 let debug_output = format!("{:?}", warning);
734 assert_eq!(
735 debug_output,
736 "WarningType::AttributeValueIsLocked(\"attr\\\"with'quotes\")"
737 );
738 }
739
740 #[test]
741 fn attribute_value_is_locked_string_with_newlines() {
742 let warning =
743 WarningType::AttributeValueIsLocked("attr\nwith\nnewlines".to_string());
744 let debug_output = format!("{:?}", warning);
745 assert_eq!(
746 debug_output,
747 "WarningType::AttributeValueIsLocked(\"attr\\nwith\\nnewlines\")"
748 );
749 }
750
751 #[test]
752 fn duplicate_id() {
753 let warning = WarningType::DuplicateId("foo".to_owned());
754 let debug_output = format!("{:?}", warning);
755 assert_eq!(debug_output, "WarningType::DuplicateId(\"foo\")");
756 }
757
758 #[test]
759 fn level0_section_heading_not_supported() {
760 let warning = WarningType::Level0SectionHeadingNotSupported;
761 let debug_output = format!("{:?}", warning);
762 assert_eq!(
763 debug_output,
764 "WarningType::Level0SectionHeadingNotSupported"
765 );
766 }
767
768 #[test]
769 fn section_heading_level_skipped() {
770 let warning = WarningType::SectionHeadingLevelSkipped(2, 4);
771 let debug_output = format!("{:?}", warning);
772 assert_eq!(
773 debug_output,
774 "WarningType::SectionHeadingLevelSkipped(2, 4)"
775 );
776 }
777
778 #[test]
779 fn section_heading_level_exceeds_maximum() {
780 let warning = WarningType::SectionHeadingLevelExceedsMaximum(6);
781 let debug_output = format!("{:?}", warning);
782 assert_eq!(
783 debug_output,
784 "WarningType::SectionHeadingLevelExceedsMaximum(6)"
785 );
786 }
787
788 #[test]
789 fn section_heading_level_out_of_range() {
790 let warning = WarningType::SectionHeadingLevelOutOfRange(-3, 1);
791 let debug_output = format!("{:?}", warning);
792 assert_eq!(
793 debug_output,
794 "WarningType::SectionHeadingLevelOutOfRange(-3, 1)"
795 );
796 }
797
798 #[test]
799 fn leveloffset_excludes_all_heading_levels() {
800 let warning = WarningType::LeveloffsetExcludesAllHeadingLevels(2147483647);
801 let debug_output = format!("{:?}", warning);
802 assert_eq!(
803 debug_output,
804 "WarningType::LeveloffsetExcludesAllHeadingLevels(2147483647)"
805 );
806 }
807
808 #[test]
809 fn list_item_out_of_sequence() {
810 let warning = WarningType::ListItemOutOfSequence("y".to_string(), "z".to_string());
811 let debug_output = format!("{:?}", warning);
812 assert_eq!(
813 debug_output,
814 "WarningType::ListItemOutOfSequence(\"y\", \"z\")"
815 );
816 }
817
818 #[test]
819 fn no_callout_found() {
820 let warning = WarningType::NoCalloutFound(2);
821 let debug_output = format!("{:?}", warning);
822 assert_eq!(debug_output, "WarningType::NoCalloutFound(2)");
823 }
824
825 #[test]
826 fn callout_list_item_out_of_sequence() {
827 let warning = WarningType::CalloutListItemOutOfSequence(2, 3);
828 let debug_output = format!("{:?}", warning);
829 assert_eq!(
830 debug_output,
831 "WarningType::CalloutListItemOutOfSequence(2, 3)"
832 );
833 }
834
835 #[test]
836 fn table_cell_exceeds_column_count() {
837 let warning = WarningType::TableCellExceedsColumnCount;
838 let debug_output = format!("{:?}", warning);
839 assert_eq!(debug_output, "WarningType::TableCellExceedsColumnCount");
840 }
841
842 #[test]
843 fn table_csv_data_has_unclosed_quote() {
844 let warning = WarningType::TableCsvDataHasUnclosedQuote;
845 let debug_output = format!("{:?}", warning);
846 assert_eq!(debug_output, "WarningType::TableCsvDataHasUnclosedQuote");
847 }
848
849 #[test]
850 fn table_missing_leading_separator() {
851 let warning = WarningType::TableMissingLeadingSeparator;
852 let debug_output = format!("{:?}", warning);
853 assert_eq!(debug_output, "WarningType::TableMissingLeadingSeparator");
854 }
855
856 #[test]
857 fn table_incomplete_row_at_end_of_table() {
858 let warning = WarningType::TableIncompleteRowAtEndOfTable;
859 let debug_output = format!("{:?}", warning);
860 assert_eq!(debug_output, "WarningType::TableIncompleteRowAtEndOfTable");
861 }
862
863 #[test]
864 fn skipping_reference_to_missing_attribute() {
865 let warning = WarningType::SkippingReferenceToMissingAttribute("name".to_string());
866 let debug_output = format!("{:?}", warning);
867 assert_eq!(
868 debug_output,
869 "WarningType::SkippingReferenceToMissingAttribute(\"name\")"
870 );
871 }
872
873 #[test]
874 fn invalid_substitution_type_for_stem_macro() {
875 let warning = WarningType::InvalidSubstitutionTypeForStemMacro("bogus".to_string());
876 let debug_output = format!("{:?}", warning);
877 assert_eq!(
878 debug_output,
879 "WarningType::InvalidSubstitutionTypeForStemMacro(\"bogus\")"
880 );
881 }
882
883 #[test]
884 fn invalid_substitution_type_for_passthrough_macro() {
885 let warning =
886 WarningType::InvalidSubstitutionTypeForPassthroughMacro("bogus".to_string());
887 let debug_output = format!("{:?}", warning);
888 assert_eq!(
889 debug_output,
890 "WarningType::InvalidSubstitutionTypeForPassthroughMacro(\"bogus\")"
891 );
892 }
893
894 #[test]
895 fn invalid_substitution_type_for_block() {
896 let warning = WarningType::InvalidSubstitutionTypeForBlock("bogus".to_string());
897 let debug_output = format!("{:?}", warning);
898 assert_eq!(
899 debug_output,
900 "WarningType::InvalidSubstitutionTypeForBlock(\"bogus\")"
901 );
902 }
903
904 #[test]
905 fn invalid_footnote_reference() {
906 let warning = WarningType::InvalidFootnoteReference("fn1".to_string());
907 let debug_output = format!("{:?}", warning);
908 assert_eq!(
909 debug_output,
910 "WarningType::InvalidFootnoteReference(\"fn1\")"
911 );
912 }
913
914 #[test]
915 fn deprecated_footnoteref_macro() {
916 let warning =
917 WarningType::DeprecatedFootnoterefMacro("footnoteref:[fn1]".to_string());
918 let debug_output = format!("{:?}", warning);
919 assert_eq!(
920 debug_output,
921 "WarningType::DeprecatedFootnoterefMacro(\"footnoteref:[fn1]\")"
922 );
923 }
924
925 #[test]
926 fn include_file_not_found() {
927 let warning = WarningType::IncludeFileNotFound("content.adoc".to_string());
928 let debug_output = format!("{:?}", warning);
929 assert_eq!(
930 debug_output,
931 "WarningType::IncludeFileNotFound(\"content.adoc\")"
932 );
933 }
934
935 #[test]
936 fn include_dropped_due_to_missing_attribute() {
937 let warning = WarningType::IncludeDroppedDueToMissingAttribute(
938 "include::{foodir}/include-file.adoc[]".to_string(),
939 );
940
941 let debug_output = format!("{:?}", warning);
942
943 assert_eq!(
944 debug_output,
945 "WarningType::IncludeDroppedDueToMissingAttribute(\"include::{foodir}/include-file.adoc[]\")"
946 );
947 }
948
949 #[test]
950 fn max_include_depth_exceeded() {
951 let warning = WarningType::MaxIncludeDepthExceeded(64);
952 let debug_output = format!("{:?}", warning);
953 assert_eq!(debug_output, "WarningType::MaxIncludeDepthExceeded(64)");
954 }
955
956 #[test]
957 fn non_utf8_include_encoding() {
958 let warning = WarningType::NonUtf8IncludeEncoding("iso-8859-1".to_string());
959 let debug_output = format!("{:?}", warning);
960 assert_eq!(
961 debug_output,
962 "WarningType::NonUtf8IncludeEncoding(\"iso-8859-1\")"
963 );
964 }
965
966 #[test]
967 fn malformed_conditional_directive() {
968 let warning = WarningType::MalformedConditionalDirective(
969 "missing target".to_string(),
970 "ifdef::[]".to_string(),
971 );
972 let debug_output = format!("{:?}", warning);
973 assert_eq!(
974 debug_output,
975 "WarningType::MalformedConditionalDirective(\"missing target\", \"ifdef::[]\")"
976 );
977 }
978
979 #[test]
980 fn unmatched_conditional_directive() {
981 let warning =
982 WarningType::UnmatchedConditionalDirective("endif::on-quest[]".to_string());
983 let debug_output = format!("{:?}", warning);
984 assert_eq!(
985 debug_output,
986 "WarningType::UnmatchedConditionalDirective(\"endif::on-quest[]\")"
987 );
988 }
989
990 #[test]
991 fn mismatched_conditional_directive() {
992 let warning =
993 WarningType::MismatchedConditionalDirective("endif::on-journey[]".to_string());
994 let debug_output = format!("{:?}", warning);
995 assert_eq!(
996 debug_output,
997 "WarningType::MismatchedConditionalDirective(\"endif::on-journey[]\")"
998 );
999 }
1000
1001 #[test]
1002 fn unterminated_conditional_directive() {
1003 let warning =
1004 WarningType::UnterminatedConditionalDirective("ifdef::on-quest[]".to_string());
1005 let debug_output = format!("{:?}", warning);
1006 assert_eq!(
1007 debug_output,
1008 "WarningType::UnterminatedConditionalDirective(\"ifdef::on-quest[]\")"
1009 );
1010 }
1011
1012 #[test]
1013 fn include_tag_not_found() {
1014 let warning = WarningType::IncludeTagNotFound("tag 'no-such-tag'".to_string());
1015 let debug_output = format!("{:?}", warning);
1016 assert_eq!(
1017 debug_output,
1018 "WarningType::IncludeTagNotFound(\"tag 'no-such-tag'\")"
1019 );
1020 }
1021
1022 #[test]
1023 fn include_tag_unclosed() {
1024 let warning = WarningType::IncludeTagUnclosed("'a'".to_string());
1025 let debug_output = format!("{:?}", warning);
1026 assert_eq!(debug_output, "WarningType::IncludeTagUnclosed(\"'a'\")");
1027 }
1028
1029 #[test]
1030 fn include_tag_mismatched_end() {
1031 let warning =
1032 WarningType::IncludeTagMismatchedEnd("'b'".to_string(), "'a'".to_string());
1033 let debug_output = format!("{:?}", warning);
1034 assert_eq!(
1035 debug_output,
1036 "WarningType::IncludeTagMismatchedEnd(\"'b'\", \"'a'\")"
1037 );
1038 }
1039
1040 #[test]
1041 fn include_tag_unexpected_end() {
1042 let warning = WarningType::IncludeTagUnexpectedEnd("'a'".to_string());
1043 let debug_output = format!("{:?}", warning);
1044 assert_eq!(
1045 debug_output,
1046 "WarningType::IncludeTagUnexpectedEnd(\"'a'\")"
1047 );
1048 }
1049
1050 #[test]
1051 fn abstract_block_in_book_without_doctitle() {
1052 let warning = WarningType::AbstractBlockInBookWithoutDoctitle;
1053 let debug_output = format!("{:?}", warning);
1054 assert_eq!(
1055 debug_output,
1056 "WarningType::AbstractBlockInBookWithoutDoctitle"
1057 );
1058 }
1059
1060 #[test]
1061 fn possible_invalid_reference() {
1062 let warning = WarningType::PossibleInvalidReference("foobaz".to_string());
1063 let debug_output = format!("{:?}", warning);
1064 assert_eq!(
1065 debug_output,
1066 "WarningType::PossibleInvalidReference(\"foobaz\")"
1067 );
1068 }
1069 }
1070 }
1071
1072 mod match_and_warnings {
1073 use crate::warnings::{MatchAndWarnings, Warning, WarningType};
1074
1075 #[test]
1076 fn impl_clone() {
1077 let maw1 = MatchAndWarnings {
1079 item: "xyz",
1080 warnings: vec![Warning {
1081 source: crate::Span::new("abc"),
1082 warning: WarningType::EmptyAttributeValue,
1083 origin: None,
1084 }],
1085 };
1086
1087 let maw2 = maw1.clone();
1088 assert_eq!(maw1, maw2);
1089 }
1090
1091 #[test]
1092 fn unwrap_if_no_warnings() {
1093 let maw = MatchAndWarnings {
1094 item: "xyz",
1095 warnings: vec![],
1096 };
1097
1098 let item = maw.unwrap_if_no_warnings();
1099 assert_eq!(item, "xyz");
1100 }
1101
1102 #[test]
1103 #[should_panic]
1104 fn unwrap_if_no_warnings_panic() {
1105 let maw = MatchAndWarnings {
1106 item: "xyz",
1107 warnings: vec![Warning {
1108 source: crate::Span::new("abc"),
1109 warning: WarningType::EmptyAttributeValue,
1110 origin: None,
1111 }],
1112 };
1113
1114 let _ = maw.unwrap_if_no_warnings();
1115 }
1117 }
1118}