Skip to main content

syntaqlite_syntax/parser/
types.rs

1// Copyright 2025 The syntaqlite Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0.
3
4use crate::grammar::TypedGrammar;
5
6/// Tri-state parse result for statement-oriented parser APIs.
7///
8/// Mirrors C parser return codes:
9/// - [`ParseOutcome::Done`]  -> `SYNTAQLITE_PARSE_DONE`
10/// - [`ParseOutcome::Ok`]    -> `SYNTAQLITE_PARSE_OK`
11/// - [`ParseOutcome::Err`]   -> `SYNTAQLITE_PARSE_ERROR`
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ParseOutcome<T, E> {
14    /// No more statements/results are available.
15    Done,
16    /// A statement parsed successfully.
17    Ok(T),
18    /// A statement parsed with an error.
19    Err(E),
20}
21
22impl<T, E> ParseOutcome<T, E> {
23    /// Convert into `Result<Option<T>, E>` for `?`-friendly control flow.
24    ///
25    /// # Errors
26    ///
27    /// Returns `Err(e)` when the outcome is [`ParseOutcome::Err`].
28    pub fn transpose(self) -> Result<Option<T>, E> {
29        match self {
30            ParseOutcome::Done => Ok(None),
31            ParseOutcome::Ok(v) => Ok(Some(v)),
32            ParseOutcome::Err(e) => Err(e),
33        }
34    }
35
36    /// Map the `Ok(T)` payload.
37    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ParseOutcome<U, E> {
38        match self {
39            ParseOutcome::Done => ParseOutcome::Done,
40            ParseOutcome::Ok(v) => ParseOutcome::Ok(f(v)),
41            ParseOutcome::Err(e) => ParseOutcome::Err(e),
42        }
43    }
44
45    /// Map the `Err(E)` payload.
46    pub fn map_err<F>(self, f: impl FnOnce(E) -> F) -> ParseOutcome<T, F> {
47        match self {
48            ParseOutcome::Done => ParseOutcome::Done,
49            ParseOutcome::Ok(v) => ParseOutcome::Ok(v),
50            ParseOutcome::Err(e) => ParseOutcome::Err(f(e)),
51        }
52    }
53}
54
55/// SQL comment style.
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum CommentKind {
58    /// A line comment starting with `--`.
59    Line,
60    /// A block comment delimited by `/* ... */`.
61    Block,
62}
63
64/// Comment captured from source during parsing.
65///
66/// Returned by [`super::TypedParsedStatement::comments`]. Requires
67/// `collect_tokens: true` in [`super::ParserConfig`].
68#[derive(Debug, Clone, Copy)]
69pub struct Comment<'a> {
70    text: &'a str,
71    kind: CommentKind,
72    offset: u32,
73    length: u32,
74}
75
76impl<'a> Comment<'a> {
77    pub(super) fn new(text: &'a str, kind: CommentKind, offset: u32, length: u32) -> Self {
78        Comment {
79            text,
80            kind,
81            offset,
82            length,
83        }
84    }
85
86    /// The full comment text, including delimiters.
87    pub fn text(&self) -> &'a str {
88        self.text
89    }
90
91    /// Whether this is a line (`--`) or block (`/* */`) comment.
92    pub fn kind(&self) -> CommentKind {
93        self.kind
94    }
95
96    /// Byte offset of the comment start within the statement source.
97    pub fn offset(&self) -> u32 {
98        self.offset
99    }
100
101    /// Byte length of the comment text.
102    pub fn length(&self) -> u32 {
103        self.length
104    }
105}
106
107/// Lightweight comment descriptor without a source text borrow.
108///
109/// Returned by [`super::AnyParsedStatement::comment_spans`].
110/// Use this when you only need position and kind, not the text.
111#[derive(Debug, Clone, Copy)]
112pub struct CommentSpan {
113    offset: u32,
114    length: u32,
115    kind: CommentKind,
116}
117
118impl CommentSpan {
119    /// Byte offset of the comment start within the statement source.
120    pub fn offset(&self) -> u32 {
121        self.offset
122    }
123
124    /// Byte length of the comment text.
125    pub fn length(&self) -> u32 {
126        self.length
127    }
128
129    /// Whether this is a line (`--`) or block (`/* */`) comment.
130    pub fn kind(&self) -> CommentKind {
131        self.kind
132    }
133
134    pub(super) fn new(offset: u32, length: u32, kind: CommentKind) -> Self {
135        CommentSpan {
136            offset,
137            length,
138            kind,
139        }
140    }
141}
142
143pub use crate::grammar::ParserTokenFlags;
144
145/// Token captured from a parsed statement, typed by grammar `G`.
146///
147/// Returned by [`super::TypedParsedStatement::tokens`]. Requires
148/// `collect_tokens: true` in [`super::ParserConfig`].
149#[derive(Debug, Clone, Copy)]
150pub struct TypedParserToken<'a, G: TypedGrammar> {
151    text: &'a str,
152    token_type: G::Token,
153    flags: ParserTokenFlags,
154    offset: u32,
155    length: u32,
156}
157
158impl<'a, G: TypedGrammar> TypedParserToken<'a, G> {
159    pub(super) fn new(
160        text: &'a str,
161        token_type: G::Token,
162        flags: ParserTokenFlags,
163        offset: u32,
164        length: u32,
165    ) -> Self {
166        TypedParserToken {
167            text,
168            token_type,
169            flags,
170            offset,
171            length,
172        }
173    }
174
175    /// The source text slice covered by this token.
176    pub fn text(&self) -> &'a str {
177        self.text
178    }
179
180    /// Grammar-typed token variant.
181    pub fn token_type(&self) -> G::Token {
182        self.token_type
183    }
184
185    /// Semantic usage flags inferred by the parser.
186    pub fn flags(&self) -> ParserTokenFlags {
187        self.flags
188    }
189
190    /// Byte offset of the token start within the statement source.
191    pub fn offset(&self) -> u32 {
192        self.offset
193    }
194
195    /// Byte length of the token text.
196    pub fn length(&self) -> u32 {
197        self.length
198    }
199}
200
201/// Parser-token alias for grammar-independent pipelines.
202pub type AnyParserToken<'a> = TypedParserToken<'a, crate::grammar::AnyGrammar>;
203
204/// Byte range of a macro call that contributed to this parse.
205///
206/// Returned by [`super::AnyParsedStatement::macro_regions`].
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub struct MacroRegion {
209    /// Byte offset of the macro call in the original source.
210    pub(crate) call_offset: u32,
211    /// Byte length of the entire macro call.
212    pub(crate) call_length: u32,
213}
214
215impl MacroRegion {
216    /// Byte offset of the macro call in the original source.
217    pub fn call_offset(&self) -> u32 {
218        self.call_offset
219    }
220    /// Byte length of the entire macro call.
221    pub fn call_length(&self) -> u32 {
222        self.call_length
223    }
224}
225
226/// Parser's best guess about what kind of token fits next.
227///
228/// Returned by incremental parse sessions for completion engines.
229#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
230#[repr(u32)]
231pub enum CompletionContext {
232    /// Could not determine context.
233    #[default]
234    Unknown = 0,
235    /// Parser expects an expression.
236    Expression = 1,
237    /// Parser expects a table reference.
238    TableRef = 2,
239}
240
241impl CompletionContext {
242    /// Convert from a numeric completion-context code.
243    ///
244    /// Mostly useful for FFI and serialization boundaries.
245    pub fn from_raw(v: u32) -> Self {
246        match v {
247            1 => Self::Expression,
248            2 => Self::TableRef,
249            _ => Self::Unknown,
250        }
251    }
252
253    /// Return the numeric completion-context code.
254    ///
255    /// Mostly useful for FFI and serialization boundaries.
256    pub fn raw(self) -> u32 {
257        self as u32
258    }
259}
260
261impl From<CompletionContext> for u32 {
262    fn from(v: CompletionContext) -> u32 {
263        v.raw()
264    }
265}