pub struct BreadcrumbGenerator<'a> {
module_path: &'a [String],
crate_name: &'a str,
}
impl<'a> BreadcrumbGenerator<'a> {
#[must_use]
pub const fn new(module_path: &'a [String], crate_name: &'a str) -> Self {
Self {
module_path,
crate_name,
}
}
#[must_use]
pub fn generate(&self) -> String {
if self.module_path.is_empty() {
return String::new();
}
let mut crumbs = Vec::new();
let depth = self.module_path.len();
crumbs.push(format!(
"[{}]({}index.md)",
self.crate_name,
"../".repeat(depth)
));
for (i, segment) in self.module_path.iter().enumerate() {
let levels_up = depth - i - 1;
let link = if levels_up == 0 {
format!("[{segment}](index.md)")
} else {
format!("[{segment}]({}index.md)", "../".repeat(levels_up))
};
crumbs.push(link);
}
format!("*{}*\n\n---\n\n", crumbs.join(" / "))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_breadcrumbs_empty_path() {
let generator = BreadcrumbGenerator::new(&[], "my_crate");
assert_eq!(generator.generate(), "");
}
#[test]
fn test_breadcrumbs_single_level() {
let path = vec!["module".to_string()];
let generator = BreadcrumbGenerator::new(&path, "my_crate");
assert_eq!(
generator.generate(),
"*[my_crate](../index.md) / [module](index.md)*\n\n---\n\n"
);
}
#[test]
fn test_breadcrumbs_two_levels() {
let path = vec!["parent".to_string(), "child".to_string()];
let generator = BreadcrumbGenerator::new(&path, "my_crate");
assert_eq!(
generator.generate(),
"*[my_crate](../../index.md) / [parent](../index.md) / [child](index.md)*\n\n---\n\n"
);
}
#[test]
fn test_breadcrumbs_three_levels() {
let path = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let generator = BreadcrumbGenerator::new(&path, "crate");
assert_eq!(
generator.generate(),
"*[crate](../../../index.md) / [a](../../index.md) / [b](../index.md) / [c](index.md)*\n\n---\n\n"
);
}
}