use crate::parser::{slugify_page_name, PageId};
use std::collections::HashMap;
use super::PageStore;
pub fn build_link_indices(store: &mut PageStore) -> HashMap<String, String> {
let page_info: Vec<(String, Vec<String>, Vec<String>, Option<String>, Option<String>)> = store
.pages
.iter()
.map(|(id, page)| {
(
id.clone(),
page.outgoing_links.clone(),
page.meta.tags.clone(),
page.namespace.clone(),
page.subgraph.clone(),
)
})
.collect();
let mut original_names: HashMap<String, String> = HashMap::new();
for (id, _, _, _, _) in &page_info {
store.backlinks.entry(id.clone()).or_default();
}
for (id, outgoing, tags, namespace, subgraph) in &page_info {
let mut forward = Vec::new();
for link_name in outgoing {
let resolved =
resolve_link(link_name, namespace.as_deref(), subgraph.as_deref(), store);
if !store.pages.contains_key(&resolved) {
original_names
.entry(resolved.clone())
.or_insert_with(|| link_name.clone());
}
if !forward.contains(&resolved) {
forward.push(resolved.clone());
}
store
.backlinks
.entry(resolved)
.or_default()
.push(id.clone());
}
for tag in tags {
let resolved =
resolve_link(tag, namespace.as_deref(), subgraph.as_deref(), store);
if !store.pages.contains_key(&resolved) {
original_names
.entry(resolved.clone())
.or_insert_with(|| tag.clone());
}
if !forward.contains(&resolved) {
forward.push(resolved.clone());
}
store
.backlinks
.entry(resolved)
.or_default()
.push(id.clone());
}
store.forward_links.insert(id.clone(), forward);
}
for backlinks in store.backlinks.values_mut() {
backlinks.sort();
backlinks.dedup();
}
original_names
}
pub fn resolve_link(
name: &str,
source_namespace: Option<&str>,
source_subgraph: Option<&str>,
store: &PageStore,
) -> String {
let target_slug = slugify_page_name(name);
let suffix = format!("/{}", target_slug);
let source_prefix = source_namespace
.map(slugify_page_name)
.unwrap_or_default();
let score_for = |id: &str| -> usize {
let shared = id
.split('/')
.zip(source_prefix.split('/'))
.take_while(|(a, b)| a == b)
.count();
shared * 10_000 + (10_000usize.saturating_sub(id.len()))
};
let mut best: Option<(usize, String)> = None;
let mut consider = |id: String| {
let s = score_for(&id);
if best.as_ref().map(|(b, _)| s > *b).unwrap_or(true) {
best = Some((s, id));
}
};
for id in store.pages.keys() {
if id == &target_slug || id.ends_with(&suffix) {
consider(id.clone());
}
}
if let Some(canonical) = store.alias_map.get(&target_slug) {
consider(canonical.clone());
}
if let Some((_, id)) = best {
return id;
}
if let Some(sg) = source_subgraph {
let sg_slug = slugify_page_name(&format!("{}/{}", sg, name));
if store.pages.contains_key(&sg_slug) {
return sg_slug;
}
}
target_slug
}