Skip to main content

ryo_symbol/
span.rs

1//! File span (location information within a file)
2
3use crate::file_path::WorkspaceFilePath;
4use crate::path::SymbolPath;
5use serde::Serialize;
6
7/// Position information within a file
8///
9/// Represents a byte range within a source file.
10///
11/// NOTE: Deserialize is NOT derived because WorkspaceFilePath requires
12/// context (workspace_root) during deserialization.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
14pub struct FileSpan {
15    /// The file containing this span
16    pub file: WorkspaceFilePath,
17    /// Start byte offset (inclusive)
18    pub start: u32,
19    /// End byte offset (exclusive)
20    pub end: u32,
21}
22
23impl FileSpan {
24    /// Create a new FileSpan
25    pub fn new(file: WorkspaceFilePath, start: u32, end: u32) -> Self {
26        Self { file, start, end }
27    }
28
29    /// Get the length of the span in bytes
30    pub fn len(&self) -> u32 {
31        self.end.saturating_sub(self.start)
32    }
33
34    /// Check if the span is empty
35    pub fn is_empty(&self) -> bool {
36        self.start >= self.end
37    }
38
39    /// Check if this span contains a byte offset
40    pub fn contains(&self, offset: u32) -> bool {
41        offset >= self.start && offset < self.end
42    }
43
44    /// Check if this span overlaps with another
45    pub fn overlaps(&self, other: &FileSpan) -> bool {
46        self.file == other.file && self.start < other.end && other.start < self.end
47    }
48}
49
50impl std::fmt::Display for FileSpan {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        write!(f, "{}:{}-{}", self.file, self.start, self.end)
53    }
54}
55
56/// Visibility of a symbol
57#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Default)]
58pub enum Visibility {
59    /// pub
60    Public,
61    /// pub(crate)
62    Crate,
63    /// pub(super)
64    Super,
65    /// pub(in path) - visible within specified path
66    Restricted(Box<SymbolPath>),
67    /// private (default)
68    #[default]
69    Private,
70}
71
72impl Visibility {
73    /// Check if this visibility is public.
74    #[inline]
75    pub fn is_public(&self) -> bool {
76        matches!(self, Visibility::Public)
77    }
78
79    /// Check if this visibility is at least crate-visible.
80    #[inline]
81    pub fn is_crate_visible(&self) -> bool {
82        matches!(self, Visibility::Public | Visibility::Crate)
83    }
84
85    /// Check if this is private.
86    #[inline]
87    pub fn is_private(&self) -> bool {
88        matches!(self, Visibility::Private)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_file_span() {
98        let file = WorkspaceFilePath::new_for_test("src/lib.rs", "/workspace", "test_crate");
99        let span = FileSpan::new(file, 10, 20);
100
101        assert_eq!(span.len(), 10);
102        assert!(!span.is_empty());
103        assert!(span.contains(10));
104        assert!(span.contains(19));
105        assert!(!span.contains(20));
106    }
107
108    #[test]
109    fn test_span_overlap() {
110        let file = WorkspaceFilePath::new_for_test("src/lib.rs", "/workspace", "test_crate");
111        let span1 = FileSpan::new(file.clone(), 10, 20);
112        let span2 = FileSpan::new(file.clone(), 15, 25);
113        let span3 = FileSpan::new(file.clone(), 20, 30);
114
115        assert!(span1.overlaps(&span2));
116        assert!(!span1.overlaps(&span3)); // Adjacent but not overlapping
117    }
118}