Improved navigation sorting
This commit is contained in:
parent
6eae5cd79b
commit
46d579247b
1 changed files with 107 additions and 38 deletions
|
@ -7,13 +7,20 @@ use super::compiler::CompiledDocument;
|
||||||
use super::compiler::Target;
|
use super::compiler::Target;
|
||||||
use super::postprocess::PostProcess;
|
use super::postprocess::PostProcess;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||||
pub struct NavEntry {
|
pub struct NavEntry {
|
||||||
pub(self) entries: Vec<(String, String, Option<String>)>,
|
title: String,
|
||||||
pub(self) children: HashMap<String, NavEntry>,
|
path: String,
|
||||||
|
previous: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavEntry {
|
#[derive(Debug, Default)]
|
||||||
|
pub struct NavEntries {
|
||||||
|
pub(self) entries: Vec<NavEntry>,
|
||||||
|
pub(self) children: HashMap<String, NavEntries>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavEntries {
|
||||||
// FIXME: Sanitize
|
// FIXME: Sanitize
|
||||||
pub fn compile(&self, target: Target, doc: &RefCell<CompiledDocument>) -> String {
|
pub fn compile(&self, target: Target, doc: &RefCell<CompiledDocument>) -> String {
|
||||||
let doc_borrow = doc.borrow();
|
let doc_borrow = doc.borrow();
|
||||||
|
@ -36,16 +43,16 @@ impl NavEntry {
|
||||||
categories: &Vec<&str>,
|
categories: &Vec<&str>,
|
||||||
did_match: bool,
|
did_match: bool,
|
||||||
result: &mut String,
|
result: &mut String,
|
||||||
entry: &NavEntry,
|
entry: &NavEntries,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) {
|
) {
|
||||||
// Orphans = Links
|
// Orphans = Links
|
||||||
for (title, path, _) in &entry.entries {
|
for entry in &entry.entries {
|
||||||
result.push_str(
|
result.push_str(
|
||||||
format!(
|
format!(
|
||||||
r#"<li><a href="{}">{}</a></li>"#,
|
r#"<li><a href="{}">{}</a></li>"#,
|
||||||
Compiler::sanitize(target, path),
|
Compiler::sanitize(target, entry.path.as_str()),
|
||||||
Compiler::sanitize(target, title)
|
Compiler::sanitize(target, entry.title.as_str())
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
);
|
||||||
|
@ -83,34 +90,46 @@ impl NavEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_entry(
|
fn sort_entry(
|
||||||
left: &(String, String, Option<String>),
|
entrymap: &HashMap<String, Option<String>>,
|
||||||
right: &(String, String, Option<String>),
|
left_title: &str,
|
||||||
|
right_title: &str,
|
||||||
) -> std::cmp::Ordering {
|
) -> std::cmp::Ordering {
|
||||||
match (&left.2, &right.2) {
|
let left_previous = entrymap.get(left_title).unwrap();
|
||||||
(Some(_), Some(_)) => left.0.cmp(&right.0),
|
let right_previous = entrymap.get(right_title).unwrap();
|
||||||
|
|
||||||
|
match (left_previous, right_previous) {
|
||||||
|
(Some(lp), Some(rp)) => {
|
||||||
|
if lp.as_str() == right_title {
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
} else if rp.as_str() == left_title {
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
} else {
|
||||||
|
Self::sort_entry(entrymap, lp.as_str(), rp.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
(Some(lp), None) => {
|
(Some(lp), None) => {
|
||||||
if &right.0 == lp {
|
if right_title == lp.as_str() {
|
||||||
std::cmp::Ordering::Greater
|
std::cmp::Ordering::Greater
|
||||||
} else {
|
} else {
|
||||||
left.0.cmp(&right.0)
|
left_title.cmp(right_title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(None, Some(rp)) => {
|
(None, Some(rp)) => {
|
||||||
if &left.0 == rp {
|
if left_title == rp.as_str() {
|
||||||
std::cmp::Ordering::Less
|
std::cmp::Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
left.0.cmp(&right.0)
|
left_title.cmp(right_title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(None, None) => left.0.cmp(&right.0),
|
(None, None) => left_title.cmp(right_title),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_navigation(
|
pub fn create_navigation(
|
||||||
docs: &Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>,
|
docs: &Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>,
|
||||||
) -> Result<NavEntry, String> {
|
) -> Result<NavEntries, String> {
|
||||||
let mut nav = NavEntry {
|
let mut nav = NavEntries {
|
||||||
entries: vec![],
|
entries: vec![],
|
||||||
children: HashMap::new(),
|
children: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
@ -153,7 +172,7 @@ pub fn create_navigation(
|
||||||
Some(cat_ent) => cat_ent,
|
Some(cat_ent) => cat_ent,
|
||||||
None => {
|
None => {
|
||||||
// Insert
|
// Insert
|
||||||
nav.children.insert(cat.clone(), NavEntry::default());
|
nav.children.insert(cat.clone(), NavEntries::default());
|
||||||
nav.children.get_mut(cat.as_str()).unwrap()
|
nav.children.get_mut(cat.as_str()).unwrap()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -162,7 +181,9 @@ pub fn create_navigation(
|
||||||
Some(subcat_ent) => subcat_ent,
|
Some(subcat_ent) => subcat_ent,
|
||||||
None => {
|
None => {
|
||||||
// Insert
|
// Insert
|
||||||
cat_ent.children.insert(subcat.clone(), NavEntry::default());
|
cat_ent
|
||||||
|
.children
|
||||||
|
.insert(subcat.clone(), NavEntries::default());
|
||||||
cat_ent.children.get_mut(subcat.as_str()).unwrap()
|
cat_ent.children.get_mut(subcat.as_str()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +192,7 @@ pub fn create_navigation(
|
||||||
Some(cat_ent) => cat_ent,
|
Some(cat_ent) => cat_ent,
|
||||||
None => {
|
None => {
|
||||||
// Insert
|
// Insert
|
||||||
nav.children.insert(cat.clone(), NavEntry::default());
|
nav.children.insert(cat.clone(), NavEntries::default());
|
||||||
nav.children.get_mut(cat.as_str()).unwrap()
|
nav.children.get_mut(cat.as_str()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,13 +201,13 @@ pub fn create_navigation(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find duplicates titles in current parent
|
// Find duplicates titles in current parent
|
||||||
for (ent_title, _, _) in &pent.entries {
|
for entry in &pent.entries {
|
||||||
if ent_title == title {
|
if &entry.title == title {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Conflicting entry title `{title}` for entries with the same parent: ({})",
|
"Conflicting entry title `{title}` for entries with the same parent: ({})",
|
||||||
pent.entries
|
pent.entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(title, _, _)| title.clone())
|
.map(|entry| entry.title.clone())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
));
|
));
|
||||||
|
@ -199,13 +220,22 @@ pub fn create_navigation(
|
||||||
}
|
}
|
||||||
all_paths.insert(path.clone(), title.clone());
|
all_paths.insert(path.clone(), title.clone());
|
||||||
|
|
||||||
pent.entries.push((title.clone(), path.clone(), previous));
|
pent.entries.push(NavEntry {
|
||||||
|
title: title.clone(),
|
||||||
|
path: path.clone(),
|
||||||
|
previous,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort entries
|
// Sort entries
|
||||||
fn sort_entries(nav: &mut NavEntry) {
|
fn sort_entries(nav: &mut NavEntries) {
|
||||||
|
let mut entrymap = nav
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.map(|ent| (ent.title.clone(), ent.previous.clone()))
|
||||||
|
.collect::<HashMap<String, Option<String>>>();
|
||||||
nav.entries
|
nav.entries
|
||||||
.sort_unstable_by(NavEntry::sort_entry);
|
.sort_by(|l, r| NavEntries::sort_entry(&entrymap, l.title.as_str(), r.title.as_str()));
|
||||||
|
|
||||||
for (_, child) in &mut nav.children {
|
for (_, child) in &mut nav.children {
|
||||||
sort_entries(child);
|
sort_entries(child);
|
||||||
|
@ -227,12 +257,32 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sort() {
|
fn sort() {
|
||||||
let entries: Vec<(String, String, Option<String>)> = vec![
|
let entries: Vec<NavEntry> = vec![
|
||||||
("Index".into(), "".into(), None),
|
NavEntry {
|
||||||
("AB".into(), "".into(), Some("Index".into())),
|
title: "Index".into(),
|
||||||
("Getting Started".into(), "".into(), Some("Index".into())),
|
path: "".into(),
|
||||||
("Sections".into(), "".into(), Some("Getting Started".into())),
|
previous: None,
|
||||||
("Style".into(), "".into(), Some("Getting Started".into())),
|
},
|
||||||
|
NavEntry {
|
||||||
|
title: "AB".into(),
|
||||||
|
path: "".into(),
|
||||||
|
previous: Some("Index".into()),
|
||||||
|
},
|
||||||
|
NavEntry {
|
||||||
|
title: "Getting Started".into(),
|
||||||
|
path: "".into(),
|
||||||
|
previous: Some("Index".into()),
|
||||||
|
},
|
||||||
|
NavEntry {
|
||||||
|
title: "Sections".into(),
|
||||||
|
path: "".into(),
|
||||||
|
previous: Some("Getting Started".into()),
|
||||||
|
},
|
||||||
|
NavEntry {
|
||||||
|
title: "Style".into(),
|
||||||
|
path: "".into(),
|
||||||
|
previous: Some("Getting Started".into()),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let mut shuffled = entries.clone();
|
let mut shuffled = entries.clone();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
|
@ -241,7 +291,14 @@ mod tests {
|
||||||
shuffled.swap(i, pos as usize);
|
shuffled.swap(i, pos as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
shuffled.sort_by(|l, r| NavEntry::sort_entry(l, r));
|
let mut entrymap = shuffled
|
||||||
|
.iter()
|
||||||
|
.map(|ent| (ent.title.clone(), ent.previous.clone()))
|
||||||
|
.collect::<HashMap<String, Option<String>>>();
|
||||||
|
|
||||||
|
shuffled.sort_by(|l, r| {
|
||||||
|
NavEntries::sort_entry(&entrymap, l.title.as_str(), r.title.as_str())
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(shuffled, entries);
|
assert_eq!(shuffled, entries);
|
||||||
}
|
}
|
||||||
|
@ -281,9 +338,21 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
nav.children.get("First").unwrap().entries,
|
nav.children.get("First").unwrap().entries,
|
||||||
vec![
|
vec![
|
||||||
("A".to_string(), "1.html".to_string(), None,),
|
NavEntry {
|
||||||
("B".to_string(), "2.html".to_string(), None,),
|
title: "A".to_string(),
|
||||||
("C".to_string(), "0.html".to_string(), None,),
|
path: "1.html".to_string(),
|
||||||
|
previous: None
|
||||||
|
},
|
||||||
|
NavEntry {
|
||||||
|
title: "B".to_string(),
|
||||||
|
path: "2.html".to_string(),
|
||||||
|
previous: None
|
||||||
|
},
|
||||||
|
NavEntry {
|
||||||
|
title: "C".to_string(),
|
||||||
|
path: "0.html".to_string(),
|
||||||
|
previous: None
|
||||||
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue