code_moniker_cli/
lines.rs1pub fn line_range(source: &str, start: u32, end: u32) -> (u32, u32) {
5 let bytes = source.as_bytes();
6 let s = (start as usize).min(bytes.len());
7 let e = (end as usize).min(bytes.len()).max(s);
8 let start_line = 1 + bytes[..s].iter().filter(|b| **b == b'\n').count() as u32;
9 let last = if e > s { e - 1 } else { s };
10 let end_line = 1 + bytes[..last.min(bytes.len())]
11 .iter()
12 .filter(|b| **b == b'\n')
13 .count() as u32;
14 (start_line, end_line)
15}
16
17#[cfg(test)]
18mod tests {
19 use super::*;
20
21 #[test]
22 fn single_line_def_is_one_line() {
23 let s = "alpha\nbeta\ngamma\n";
24 assert_eq!(line_range(s, 0, 5), (1, 1));
25 }
26
27 #[test]
28 fn multi_line_def_spans_lines_inclusive() {
29 let s = "alpha\nbeta\ngamma\n";
30 assert_eq!(line_range(s, 0, 11), (1, 2));
31 }
32
33 #[test]
34 fn def_starting_on_line_three() {
35 let s = "a\nb\nc\nd\n";
36 assert_eq!(line_range(s, 4, 5), (3, 3));
37 }
38
39 #[test]
40 fn def_ending_at_eof_without_newline() {
41 let s = "a\nb\nc";
42 assert_eq!(line_range(s, 4, 5), (3, 3));
43 }
44
45 #[test]
46 fn out_of_bounds_clamps_safely() {
47 let s = "a\nb\n";
48 assert_eq!(line_range(s, 100, 200), (3, 3));
49 }
50
51 #[test]
52 fn end_before_start_collapses_to_start() {
53 let s = "a\nb\nc\n";
54 assert_eq!(line_range(s, 4, 2), (3, 3));
55 }
56}