Skip to main content

agent_citation/
attribute.rs

1#[derive(Debug, Clone, PartialEq, Eq)]
2pub struct Marker {
3    pub id: String,
4    pub start: usize,
5    pub end: usize,
6}
7
8pub fn attribute(text: &str) -> Vec<Marker> {
9    let bytes = text.as_bytes();
10    let mut out: Vec<Marker> = Vec::new();
11    let mut i = 0usize;
12    while i < bytes.len() {
13        if bytes[i] != b'[' {
14            i += 1;
15            continue;
16        }
17        // Scan inner until ']' or end. Accept digit groups separated by commas + spaces.
18        let start = i;
19        let mut j = i + 1;
20        let mut ids: Vec<String> = Vec::new();
21        let mut cur: String = String::new();
22        let mut saw_digit_in_part = false;
23        let mut valid = true;
24        let mut closed = false;
25        while j < bytes.len() {
26            let b = bytes[j];
27            if b == b']' {
28                closed = true;
29                break;
30            }
31            if b.is_ascii_digit() {
32                cur.push(b as char);
33                saw_digit_in_part = true;
34                j += 1;
35                continue;
36            }
37            if b == b',' {
38                if !saw_digit_in_part {
39                    valid = false;
40                    break;
41                }
42                ids.push(std::mem::take(&mut cur));
43                saw_digit_in_part = false;
44                j += 1;
45                while j < bytes.len() && bytes[j] == b' ' {
46                    j += 1;
47                }
48                continue;
49            }
50            // Anything else inside is invalid for a citation marker.
51            valid = false;
52            break;
53        }
54        if !closed || !valid || !saw_digit_in_part {
55            i += 1;
56            continue;
57        }
58        ids.push(cur);
59        let end = j + 1;
60        for id in ids {
61            if !id.is_empty() {
62                out.push(Marker {
63                    id,
64                    start,
65                    end,
66                });
67            }
68        }
69        i = end;
70    }
71    out
72}
73
74pub fn unique_marker_ids(markers: &[Marker]) -> Vec<String> {
75    let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
76    let mut out: Vec<String> = Vec::new();
77    for m in markers {
78        if seen.insert(m.id.clone()) {
79            out.push(m.id.clone());
80        }
81    }
82    out
83}