agent_citation/
attribute.rs1#[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 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 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}