readstat/
rs_parser.rs

1//! Safe wrapper around the `ReadStat` C parser.
2//!
3//! [`ReadStatParser`] provides a builder-pattern API for configuring and invoking the
4//! `ReadStat` C library's `readstat_parser_t`. It manages the parser lifecycle (init/free)
5//! via RAII and exposes methods for setting callback handlers, row limits/offsets,
6//! and triggering the actual `.sas7bdat` parse.
7
8#![allow(
9    clippy::cast_possible_wrap,
10    clippy::cast_possible_truncation,
11    clippy::cast_lossless
12)]
13
14use log::debug;
15use std::os::raw::{c_char, c_long, c_void};
16
17use crate::err::{ReadStatError, check_c_error};
18
19/// Safe RAII wrapper around the `ReadStat` C parser (`readstat_parser_t`).
20///
21/// Provides a builder-pattern API for configuring callbacks, row limits/offsets,
22/// and invoking the parse. The underlying C parser is freed on drop.
23pub(crate) struct ReadStatParser {
24    parser: *mut readstat_sys::readstat_parser_t,
25}
26
27impl ReadStatParser {
28    /// Allocates and initializes a new `ReadStat` C parser.
29    pub(crate) fn new() -> Self {
30        let parser: *mut readstat_sys::readstat_parser_t =
31            unsafe { readstat_sys::readstat_parser_init() };
32
33        Self { parser }
34    }
35
36    /// Registers the callback invoked when file-level metadata is parsed.
37    pub(crate) fn set_metadata_handler(
38        self,
39        metadata_handler: readstat_sys::readstat_metadata_handler,
40    ) -> Result<Self, ReadStatError> {
41        let set_metadata_handler_error =
42            unsafe { readstat_sys::readstat_set_metadata_handler(self.parser, metadata_handler) };
43
44        debug!("After setting metadata handler, error ==> {set_metadata_handler_error}");
45
46        check_c_error(set_metadata_handler_error as i32)?;
47        Ok(self)
48    }
49
50    /// Sets the maximum number of rows to read. `None` means no limit.
51    pub(crate) fn set_row_limit(self, row_limit: Option<u32>) -> Result<Self, ReadStatError> {
52        if let Some(r) = row_limit {
53            let set_row_limit_error =
54                unsafe { readstat_sys::readstat_set_row_limit(self.parser, r as c_long) };
55
56            debug!("After setting row limit, error ==> {set_row_limit_error}");
57
58            check_c_error(set_row_limit_error as i32)?;
59        }
60        Ok(self)
61    }
62
63    /// Sets the starting row offset for reading. `None` means start from row 0.
64    pub(crate) fn set_row_offset(self, row_offset: Option<u32>) -> Result<Self, ReadStatError> {
65        if let Some(r) = row_offset {
66            let set_row_offset_error =
67                unsafe { readstat_sys::readstat_set_row_offset(self.parser, r as c_long) };
68
69            debug!("After setting row offset, error ==> {set_row_offset_error}");
70
71            check_c_error(set_row_offset_error as i32)?;
72        }
73        Ok(self)
74    }
75
76    /// Registers the callback invoked for each variable (column) definition.
77    pub(crate) fn set_variable_handler(
78        self,
79        variable_handler: readstat_sys::readstat_variable_handler,
80    ) -> Result<Self, ReadStatError> {
81        let set_variable_handler_error =
82            unsafe { readstat_sys::readstat_set_variable_handler(self.parser, variable_handler) };
83
84        debug!("After setting variable handler, error ==> {set_variable_handler_error}");
85
86        check_c_error(set_variable_handler_error as i32)?;
87        Ok(self)
88    }
89
90    /// Registers the callback invoked for each cell value during row parsing.
91    pub(crate) fn set_value_handler(
92        self,
93        value_handler: readstat_sys::readstat_value_handler,
94    ) -> Result<Self, ReadStatError> {
95        let set_value_handler_error =
96            unsafe { readstat_sys::readstat_set_value_handler(self.parser, value_handler) };
97
98        debug!("After setting value handler, error ==> {set_value_handler_error}");
99
100        check_c_error(set_value_handler_error as i32)?;
101        Ok(self)
102    }
103
104    /// Registers a custom handler for opening the data source.
105    pub(crate) fn set_open_handler(
106        self,
107        open_handler: readstat_sys::readstat_open_handler,
108    ) -> Result<Self, ReadStatError> {
109        let error = unsafe { readstat_sys::readstat_set_open_handler(self.parser, open_handler) };
110        debug!("After setting open handler, error ==> {error}");
111        check_c_error(error as i32)?;
112        Ok(self)
113    }
114
115    /// Registers a custom handler for closing the data source.
116    pub(crate) fn set_close_handler(
117        self,
118        close_handler: readstat_sys::readstat_close_handler,
119    ) -> Result<Self, ReadStatError> {
120        let error = unsafe { readstat_sys::readstat_set_close_handler(self.parser, close_handler) };
121        debug!("After setting close handler, error ==> {error}");
122        check_c_error(error as i32)?;
123        Ok(self)
124    }
125
126    /// Registers a custom handler for seeking within the data source.
127    pub(crate) fn set_seek_handler(
128        self,
129        seek_handler: readstat_sys::readstat_seek_handler,
130    ) -> Result<Self, ReadStatError> {
131        let error = unsafe { readstat_sys::readstat_set_seek_handler(self.parser, seek_handler) };
132        debug!("After setting seek handler, error ==> {error}");
133        check_c_error(error as i32)?;
134        Ok(self)
135    }
136
137    /// Registers a custom handler for reading from the data source.
138    pub(crate) fn set_read_handler(
139        self,
140        read_handler: readstat_sys::readstat_read_handler,
141    ) -> Result<Self, ReadStatError> {
142        let error = unsafe { readstat_sys::readstat_set_read_handler(self.parser, read_handler) };
143        debug!("After setting read handler, error ==> {error}");
144        check_c_error(error as i32)?;
145        Ok(self)
146    }
147
148    /// Registers a custom handler for progress updates.
149    pub(crate) fn set_update_handler(
150        self,
151        update_handler: readstat_sys::readstat_update_handler,
152    ) -> Result<Self, ReadStatError> {
153        let error =
154            unsafe { readstat_sys::readstat_set_update_handler(self.parser, update_handler) };
155        debug!("After setting update handler, error ==> {error}");
156        check_c_error(error as i32)?;
157        Ok(self)
158    }
159
160    /// Sets a custom I/O context pointer passed to all I/O handler callbacks.
161    pub(crate) fn set_io_ctx(self, io_ctx: *mut c_void) -> Result<Self, ReadStatError> {
162        let error = unsafe { readstat_sys::readstat_set_io_ctx(self.parser, io_ctx) };
163        debug!("After setting io ctx, error ==> {error}");
164        check_c_error(error as i32)?;
165        Ok(self)
166    }
167
168    /// Parses a `.sas7bdat` file, invoking registered callbacks as data is read.
169    ///
170    /// Returns the raw `ReadStat` error code. Use [`check_c_error`](crate::err::check_c_error)
171    /// to convert to a `Result`.
172    pub(crate) fn parse_sas7bdat(
173        &mut self,
174        path: *const c_char,
175        user_ctx: *mut c_void,
176    ) -> readstat_sys::readstat_error_t {
177        let parse_sas7bdat_error: readstat_sys::readstat_error_t =
178            unsafe { readstat_sys::readstat_parse_sas7bdat(self.parser, path, user_ctx) };
179
180        debug!("After calling parse sas7bdat, error ==> {parse_sas7bdat_error}");
181
182        parse_sas7bdat_error
183    }
184}
185
186impl Drop for ReadStatParser {
187    fn drop(&mut self) {
188        debug!("Freeing parser");
189
190        unsafe { readstat_sys::readstat_parser_free(self.parser) };
191    }
192}