Merge pull request #2613 from szabgab/avoid-duplicate-entries
Avoid using the same file twice in SUMMARY.md
This commit is contained in:
commit
7a1977a78c
1 changed files with 108 additions and 0 deletions
|
|
@ -3,6 +3,7 @@ use log::{debug, trace, warn};
|
|||
use memchr::Memchr;
|
||||
use pulldown_cmark::{DefaultBrokenLinkCallback, Event, HeadingLevel, Tag, TagEnd};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -245,6 +246,11 @@ impl<'a> SummaryParser<'a> {
|
|||
.parse_affix(false)
|
||||
.with_context(|| "There was an error parsing the suffix chapters")?;
|
||||
|
||||
let mut files = HashSet::new();
|
||||
for part in [&prefix_chapters, &numbered_chapters, &suffix_chapters] {
|
||||
self.check_for_duplicates(&part, &mut files)?;
|
||||
}
|
||||
|
||||
Ok(Summary {
|
||||
title,
|
||||
prefix_chapters,
|
||||
|
|
@ -253,6 +259,29 @@ impl<'a> SummaryParser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Recursively check for duplicate files in the summary items.
|
||||
fn check_for_duplicates<'b>(
|
||||
&self,
|
||||
items: &'b [SummaryItem],
|
||||
files: &mut HashSet<&'b PathBuf>,
|
||||
) -> Result<()> {
|
||||
for item in items {
|
||||
if let SummaryItem::Link(link) = item {
|
||||
if let Some(location) = &link.location {
|
||||
if !files.insert(location) {
|
||||
bail!(anyhow::anyhow!(
|
||||
"Duplicate file in SUMMARY.md: {:?}",
|
||||
location
|
||||
));
|
||||
}
|
||||
}
|
||||
// Recursively check nested items
|
||||
self.check_for_duplicates(&link.nested_items, files)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the affix chapters.
|
||||
fn parse_affix(&mut self, is_prefix: bool) -> Result<Vec<SummaryItem>> {
|
||||
let mut items = Vec::new();
|
||||
|
|
@ -1127,4 +1156,83 @@ mod tests {
|
|||
let got = parser.parse_affix(false).unwrap();
|
||||
assert_eq!(got, should_be);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_entries_1() {
|
||||
let src = r#"
|
||||
# Summary
|
||||
- [A](./a.md)
|
||||
- [A](./a.md)
|
||||
"#;
|
||||
|
||||
let res = parse_summary(src);
|
||||
assert!(res.is_err());
|
||||
let error_message = res.err().unwrap().to_string();
|
||||
assert_eq!(error_message, r#"Duplicate file in SUMMARY.md: "./a.md""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_entries_2() {
|
||||
let src = r#"
|
||||
# Summary
|
||||
- [A](./a.md)
|
||||
- [A](./a.md)
|
||||
"#;
|
||||
|
||||
let res = parse_summary(src);
|
||||
assert!(res.is_err());
|
||||
let error_message = res.err().unwrap().to_string();
|
||||
assert_eq!(error_message, r#"Duplicate file in SUMMARY.md: "./a.md""#);
|
||||
}
|
||||
#[test]
|
||||
fn duplicate_entries_3() {
|
||||
let src = r#"
|
||||
# Summary
|
||||
- [A](./a.md)
|
||||
- [B](./b.md)
|
||||
- [A](./a.md)
|
||||
"#;
|
||||
|
||||
let res = parse_summary(src);
|
||||
assert!(res.is_err());
|
||||
let error_message = res.err().unwrap().to_string();
|
||||
assert_eq!(error_message, r#"Duplicate file in SUMMARY.md: "./a.md""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_entries_4() {
|
||||
let src = r#"
|
||||
# Summary
|
||||
[A](./a.md)
|
||||
- [B](./b.md)
|
||||
- [A](./a.md)
|
||||
"#;
|
||||
|
||||
let res = parse_summary(src);
|
||||
assert!(res.is_err());
|
||||
let error_message = res.err().unwrap().to_string();
|
||||
assert_eq!(error_message, r#"Duplicate file in SUMMARY.md: "./a.md""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_entries_5() {
|
||||
let src = r#"
|
||||
# Summary
|
||||
[A](./a.md)
|
||||
|
||||
# hi
|
||||
- [B](./b.md)
|
||||
|
||||
# bye
|
||||
|
||||
---
|
||||
|
||||
[A](./a.md)
|
||||
"#;
|
||||
|
||||
let res = parse_summary(src);
|
||||
assert!(res.is_err());
|
||||
let error_message = res.err().unwrap().to_string();
|
||||
assert_eq!(error_message, r#"Duplicate file in SUMMARY.md: "./a.md""#);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue