clickhouse_lexer_sys/
lib.rs1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4use std::ffi::{c_char, c_void};
5use std::fmt;
6use std::str::Utf8Error;
7
8pub mod ffi {
10 use std::ffi::{c_char, c_int, c_uchar, c_void};
11
12 extern "C" {
13 pub static clickhouse_lexer_size: usize;
15 pub fn clickhouse_lexer_create(
17 ptr: *mut c_void,
18 begin: *const c_char,
19 end: *const c_char,
20 max_query_size: usize,
21 );
22 pub fn clickhouse_lexer_next_token(
24 ptr: *mut c_void,
25 out_token_begin: *mut *const c_char,
26 out_token_end: *mut *const c_char,
27 ) -> c_uchar;
28 pub fn clickhouse_lexer_token_is_significant(token: c_uchar) -> c_int;
30 pub fn clickhouse_lexer_token_is_error(token: c_uchar) -> c_int;
32 pub fn clickhouse_lexer_token_is_end(token: c_uchar) -> c_int;
34 }
35}
36
37include!(concat!(env!("OUT_DIR"), "/generated.rs"));
38
39impl TokenKind {
40 pub fn is_significant(self) -> bool {
42 !matches!(self, TokenKind::Whitespace | TokenKind::Comment)
43 }
44
45 pub fn is_error(self) -> bool {
47 self > TokenKind::EndOfStream
48 }
49
50 pub fn is_end(self) -> bool {
52 self == TokenKind::EndOfStream
53 }
54}
55
56impl fmt::Display for TokenKind {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.write_str(self.name())
59 }
60}
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub struct Token<'a> {
65 pub kind: TokenKind,
67 pub begin: usize,
69 pub end: usize,
71 pub text: &'a [u8],
73}
74
75impl<'a> Token<'a> {
76 pub fn text_str(&self) -> Result<&'a str, Utf8Error> {
78 std::str::from_utf8(self.text)
79 }
80}
81
82pub struct Lexer<'a> {
88 input: &'a [u8],
89 state: Box<[u64]>,
91 previous_end: usize,
92 done: bool,
93}
94
95impl<'a> Lexer<'a> {
96 pub fn new(input: &'a (impl AsRef<[u8]> + ?Sized)) -> Self {
98 Self::with_max_query_size(input, 0)
99 }
100
101 pub fn with_max_query_size(
104 input: &'a (impl AsRef<[u8]> + ?Sized),
105 max_query_size: usize,
106 ) -> Self {
107 let input = input.as_ref();
108 let size = unsafe { ffi::clickhouse_lexer_size };
110 let mut state = vec![0u64; size.div_ceil(8)].into_boxed_slice();
111 let begin = input.as_ptr().cast::<c_char>();
112 unsafe {
115 ffi::clickhouse_lexer_create(
116 state.as_mut_ptr().cast::<c_void>(),
117 begin,
118 begin.add(input.len()),
119 max_query_size,
120 );
121 }
122 Self {
123 input,
124 state,
125 previous_end: 0,
126 done: false,
127 }
128 }
129
130 pub fn input(&self) -> &'a [u8] {
132 self.input
133 }
134
135 pub fn next_token(&mut self) -> Token<'a> {
140 let mut begin = std::ptr::null();
141 let mut end = std::ptr::null();
142 let raw = unsafe {
144 ffi::clickhouse_lexer_next_token(
145 self.state.as_mut_ptr().cast::<c_void>(),
146 &mut begin,
147 &mut end,
148 )
149 };
150 let kind = TokenKind::from_raw(raw)
151 .unwrap_or_else(|| panic!("ClickHouse lexer returned unknown token type {raw}"));
152 let begin = self.offset(begin);
153 let end = self.offset(end);
154 Token {
155 kind,
156 begin,
157 end,
158 text: &self.input[begin..end],
159 }
160 }
161
162 fn offset(&self, ptr: *const c_char) -> usize {
164 let offset = (ptr as usize).wrapping_sub(self.input.as_ptr() as usize);
165 assert!(
166 offset <= self.input.len(),
167 "ClickHouse lexer returned a pointer outside the input"
168 );
169 offset
170 }
171}
172
173impl<'a> Iterator for Lexer<'a> {
174 type Item = Token<'a>;
175
176 fn next(&mut self) -> Option<Token<'a>> {
177 if self.done {
178 return None;
179 }
180 let token = self.next_token();
181 if token.kind.is_end() {
182 self.done = true;
183 return None;
184 }
185 if token.kind.is_error() {
186 self.done = true;
187 return Some(token);
188 }
189 assert!(
191 token.end > self.previous_end,
192 "ClickHouse lexer stopped advancing at byte {} of {}",
193 token.end,
194 self.input.len()
195 );
196 self.previous_end = token.end;
197 Some(token)
198 }
199}
200
201impl fmt::Debug for Lexer<'_> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 f.debug_struct("Lexer")
204 .field("input_len", &self.input.len())
205 .field("previous_end", &self.previous_end)
206 .field("done", &self.done)
207 .finish()
208 }
209}