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