Skip to main content

aft/
symbols.rs

1use serde::{Deserialize, Serialize};
2
3/// The kind of a discovered symbol.
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum SymbolKind {
7    Function,
8    Class,
9    Method,
10    Struct,
11    Interface,
12    Enum,
13    TypeAlias,
14    /// Top-level const/let variable declaration
15    Variable,
16    /// Markdown heading (h1, h2, h3, etc.)
17    Heading,
18    /// Synthetic file-level summary chunk for generic entry-point files.
19    FileSummary,
20}
21
22/// Location range within a source file (line/column, 0-indexed internally).
23///
24/// **Serialization**: JSON output is 1-based (matches editor/git conventions).
25/// All internal Rust code uses 0-indexed values. The custom `Serialize` impl
26/// adds +1 to all fields during serialization.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct Range {
29    pub start_line: u32,
30    pub start_col: u32,
31    pub end_line: u32,
32    pub end_col: u32,
33}
34
35impl serde::Serialize for Range {
36    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
37        use serde::ser::SerializeStruct;
38        let mut s = serializer.serialize_struct("Range", 4)?;
39        s.serialize_field("start_line", &(self.start_line + 1))?;
40        s.serialize_field("start_col", &(self.start_col + 1))?;
41        s.serialize_field("end_line", &(self.end_line + 1))?;
42        s.serialize_field("end_col", &(self.end_col + 1))?;
43        s.end()
44    }
45}
46
47impl<'de> serde::Deserialize<'de> for Range {
48    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
49        #[derive(Deserialize)]
50        struct SerializedRange {
51            start_line: u32,
52            start_col: u32,
53            end_line: u32,
54            end_col: u32,
55        }
56
57        let range = SerializedRange::deserialize(deserializer)?;
58        Ok(Self {
59            start_line: range.start_line.saturating_sub(1),
60            start_col: range.start_col.saturating_sub(1),
61            end_line: range.end_line.saturating_sub(1),
62            end_col: range.end_col.saturating_sub(1),
63        })
64    }
65}
66
67/// A symbol discovered in a source file.
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct Symbol {
70    pub name: String,
71    pub kind: SymbolKind,
72    pub range: Range,
73    /// Function/method signature, e.g. `fn foo(x: i32) -> bool`
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub signature: Option<String>,
76    /// Scope chain from outermost to innermost parent, e.g. `["ClassName"]` for a method.
77    pub scope_chain: Vec<String>,
78    /// Whether this symbol is exported (relevant for TS/JS).
79    pub exported: bool,
80    /// The direct parent symbol name, if any.
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub parent: Option<String>,
83}
84
85/// A resolved symbol match — a `Symbol` plus the file it was found in.
86#[derive(Debug, Clone, Serialize)]
87pub struct SymbolMatch {
88    pub symbol: Symbol,
89    pub file: String,
90}