runic_kit/span.rs
1//! This module defines the `Span` struct, which represents a span of text in a source file.
2//! It also provides utilities for working with spans.
3
4/// A `Span` represents a contiguous region in a source file, defined by its start and end byte indices.
5#[derive(Debug)]
6pub struct Span {
7 /// The starting byte index of the span (inclusive).
8 pub start: usize,
9 /// The ending byte index of the span (exclusive).
10 pub end: usize,
11}
12
13impl Span {
14 /// Creates a new `Span` from the given start and end byte indices.
15 ///
16 /// # Panics
17 ///
18 /// Panics if `start` is greater than or equal to `end`.
19 pub fn new(start: usize, end: usize) -> Self {
20 assert!(start < end, "Span start must be less than end");
21 Span { start, end }
22 }
23}
24
25/// Converts a byte index in the source string to a (line, column) tuple.
26///
27/// Lines and columns are 1-based.
28///
29/// Column of the newline character is + 1 of the last character in the line.
30///
31/// # Usage
32///
33/// ```rust
34/// use runic_kit::span::location_to_line_col;
35///
36/// let source = "Hello\nWorld";
37/// let index = 6; // Byte index of 'W'
38/// let (line, col) = location_to_line_col(source, index);
39/// assert_eq!((line, col), (2, 1)); // 'W' is on line 2, column 1
40/// ```
41pub fn location_to_line_col(source: &str, index: usize) -> (usize, usize) {
42 let mut line = 1;
43 let mut col = 1;
44
45 for (i, c) in source.char_indices() {
46 if i == index {
47 break;
48 }
49
50 if c == '\n' {
51 line += 1;
52 col = 1;
53 } else {
54 col += 1;
55 }
56 }
57
58 (line, col)
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_span_new() {
67 let span = Span::new(5, 10);
68 assert_eq!(span.start, 5);
69 assert_eq!(span.end, 10);
70 }
71
72 #[test]
73 #[should_panic(expected = "Span start must be less than end")]
74 fn test_span_new_invalid() {
75 Span::new(10, 5);
76 }
77
78 #[test]
79 fn test_location_to_line_col() {
80 let source = "Hello\nWorld";
81 assert_eq!(location_to_line_col(source, 0), (1, 1)); // 'H'
82 assert_eq!(location_to_line_col(source, 4), (1, 5)); // 'o'
83 assert_eq!(location_to_line_col(source, 5), (1, 6)); // '\n'
84 assert_eq!(location_to_line_col(source, 6), (2, 1)); // 'W'
85 assert_eq!(location_to_line_col(source, 10), (2, 5)); // 'd'
86 }
87}