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