switchback_traits/
intra_links.rs1use crate::LinkFormatter;
4use crate::link_context::LinkContext;
5use crate::{Anchor, IntraLink, LinkTarget};
6
7pub fn apply_intra_links(
10 field: &str,
11 content: &str,
12 links: &[IntraLink],
13 formatter: &dyn LinkFormatter,
14 ctx: &LinkContext,
15) -> String {
16 let mut field_links: Vec<_> = links
17 .iter()
18 .filter(|l| l.anchor.field == field && !matches!(l.target, LinkTarget::Unresolved))
19 .collect();
20 if field_links.is_empty() {
21 return content.to_string();
22 }
23 field_links.sort_by_key(|l| l.anchor.byte_start);
24 let mut out = String::new();
25 let mut cursor = 0usize;
26 for link in field_links {
27 let start = link.anchor.byte_start as usize;
28 let end = link.anchor.byte_end as usize;
29 if start < cursor || end > content.len() || start > end {
30 continue;
31 }
32 out.push_str(&content[cursor..start]);
33 out.push_str(&formatter.format(&link.target, ctx));
34 cursor = end;
35 }
36 out.push_str(&content[cursor..]);
37 out
38}
39
40pub fn links_for_field<'a>(links: &'a [IntraLink], field: &str) -> Vec<&'a IntraLink> {
42 links.iter().filter(|l| l.anchor.field == field).collect()
43}
44
45pub fn anchor(field: impl Into<String>, byte_start: u32, byte_end: u32) -> Anchor {
47 Anchor {
48 field: field.into(),
49 byte_start,
50 byte_end,
51 }
52}
53
54#[cfg(test)]
55mod tests;