Skip to main content

hdbconnect_arrow/traits/
builder.rs

1//! Builder traits for Arrow array construction.
2//!
3//! This module defines the [`HanaCompatibleBuilder`] trait that all Arrow
4//! builders must implement to accept HANA values.
5
6use arrow_array::ArrayRef;
7
8use super::sealed::private::Sealed;
9
10/// Marker trait for Arrow builders that can accept HANA values.
11///
12/// This trait is sealed to prevent external implementations that might
13/// violate invariants around null handling and type safety.
14///
15/// # Implementors
16///
17/// This trait is implemented by wrapper types in the `builders` module:
18/// - `UInt8BuilderWrapper`
19/// - `Int16BuilderWrapper`
20/// - `StringBuilderWrapper`
21/// - etc.
22///
23/// # Thread Safety
24///
25/// Implementations must be `Send` to allow parallel batch processing.
26pub trait HanaCompatibleBuilder: Sealed + Send {
27    /// Append a HANA value to this builder.
28    ///
29    /// # Errors
30    ///
31    /// Returns an error if the value cannot be converted to the target type.
32    fn append_hana_value(&mut self, value: &hdbconnect::HdbValue) -> crate::Result<()>;
33
34    /// Append a null value to this builder.
35    fn append_null(&mut self);
36
37    /// Finish building and return the Arrow array.
38    ///
39    /// After calling this method, the builder is reset and can be reused.
40    fn finish(&mut self) -> ArrayRef;
41
42    /// Reset the builder, clearing all data while preserving capacity.
43    ///
44    /// This is more efficient than calling `finish()` when you want to
45    /// reuse the builder without creating an array. Useful for batch
46    /// boundary resets where the previous batch data is discarded.
47    fn reset(&mut self) {
48        // Default implementation: call finish() and discard the result
49        let _ = self.finish();
50    }
51
52    /// Returns the number of values (including nulls) appended so far.
53    fn len(&self) -> usize;
54
55    /// Returns true if no values have been appended.
56    fn is_empty(&self) -> bool {
57        self.len() == 0
58    }
59
60    /// Returns the capacity hint for this builder, if known.
61    fn capacity(&self) -> Option<usize> {
62        None
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use hdbconnect::HdbValue;
69
70    use super::*;
71    use crate::builders::boolean::BooleanBuilderWrapper;
72    use crate::builders::primitive::{Float64BuilderWrapper, Int32BuilderWrapper};
73    use crate::builders::string::StringBuilderWrapper;
74
75    // Test that the trait is object-safe
76    fn _assert_object_safe(_: &dyn HanaCompatibleBuilder) {}
77
78    // ═══════════════════════════════════════════════════════════════════════════
79    // is_empty Tests
80    // ═══════════════════════════════════════════════════════════════════════════
81
82    #[test]
83    fn test_is_empty_initial() {
84        let builder = Int32BuilderWrapper::new(10);
85        assert!(builder.is_empty());
86    }
87
88    #[test]
89    fn test_is_empty_after_append() {
90        let mut builder = Int32BuilderWrapper::new(10);
91        builder.append_hana_value(&HdbValue::INT(42)).unwrap();
92        assert!(!builder.is_empty());
93    }
94
95    #[test]
96    fn test_is_empty_after_append_null() {
97        let mut builder = Int32BuilderWrapper::new(10);
98        builder.append_null();
99        assert!(!builder.is_empty());
100    }
101
102    #[test]
103    fn test_is_empty_after_finish() {
104        let mut builder = Int32BuilderWrapper::new(10);
105        builder.append_hana_value(&HdbValue::INT(42)).unwrap();
106        let _ = builder.finish();
107        assert!(builder.is_empty());
108    }
109
110    // ═══════════════════════════════════════════════════════════════════════════
111    // reset Tests
112    // ═══════════════════════════════════════════════════════════════════════════
113
114    #[test]
115    fn test_reset_clears_builder() {
116        let mut builder = Int32BuilderWrapper::new(10);
117        builder.append_hana_value(&HdbValue::INT(42)).unwrap();
118        builder.append_hana_value(&HdbValue::INT(43)).unwrap();
119        assert_eq!(builder.len(), 2);
120
121        builder.reset();
122        assert_eq!(builder.len(), 0);
123        assert!(builder.is_empty());
124    }
125
126    #[test]
127    fn test_reset_on_empty_builder() {
128        let mut builder = Int32BuilderWrapper::new(10);
129        builder.reset();
130        assert_eq!(builder.len(), 0);
131    }
132
133    #[test]
134    fn test_reset_allows_reuse() {
135        let mut builder = Int32BuilderWrapper::new(10);
136        builder.append_hana_value(&HdbValue::INT(42)).unwrap();
137        builder.reset();
138
139        builder.append_hana_value(&HdbValue::INT(100)).unwrap();
140        builder.append_hana_value(&HdbValue::INT(200)).unwrap();
141        assert_eq!(builder.len(), 2);
142    }
143
144    // ═══════════════════════════════════════════════════════════════════════════
145    // capacity Tests
146    // ═══════════════════════════════════════════════════════════════════════════
147
148    #[test]
149    fn test_capacity_returns_some_for_int_builder() {
150        let builder = Int32BuilderWrapper::new(100);
151        assert!(builder.capacity().is_some());
152        assert_eq!(builder.capacity(), Some(100));
153    }
154
155    #[test]
156    fn test_capacity_returns_some_for_float_builder() {
157        let builder = Float64BuilderWrapper::new(50);
158        assert!(builder.capacity().is_some());
159    }
160
161    #[test]
162    fn test_capacity_returns_none_for_string_builder() {
163        let builder = StringBuilderWrapper::new(10, 100);
164        assert!(builder.capacity().is_none());
165    }
166
167    #[test]
168    fn test_capacity_returns_some_for_boolean_builder() {
169        let builder = BooleanBuilderWrapper::new(200);
170        assert!(builder.capacity().is_some());
171    }
172
173    // ═══════════════════════════════════════════════════════════════════════════
174    // Object Safety Tests
175    // ═══════════════════════════════════════════════════════════════════════════
176
177    #[test]
178    fn test_object_safety_with_boxed_builder() {
179        let builder: Box<dyn HanaCompatibleBuilder> = Box::new(Int32BuilderWrapper::new(10));
180        assert!(builder.is_empty());
181        assert_eq!(builder.len(), 0);
182    }
183
184    #[test]
185    fn test_object_safety_multiple_types() {
186        let mut builders: Vec<Box<dyn HanaCompatibleBuilder>> = vec![
187            Box::new(Int32BuilderWrapper::new(10)),
188            Box::new(Float64BuilderWrapper::new(10)),
189            Box::new(StringBuilderWrapper::new(10, 100)),
190            Box::new(BooleanBuilderWrapper::new(10)),
191        ];
192
193        for builder in &builders {
194            assert!(builder.is_empty());
195        }
196
197        for builder in &mut builders {
198            builder.append_null();
199            assert!(!builder.is_empty());
200        }
201    }
202
203    // ═══════════════════════════════════════════════════════════════════════════
204    // len Tests
205    // ═══════════════════════════════════════════════════════════════════════════
206
207    #[test]
208    fn test_len_increments_with_values() {
209        let mut builder = Int32BuilderWrapper::new(10);
210        assert_eq!(builder.len(), 0);
211
212        builder.append_hana_value(&HdbValue::INT(1)).unwrap();
213        assert_eq!(builder.len(), 1);
214
215        builder.append_hana_value(&HdbValue::INT(2)).unwrap();
216        assert_eq!(builder.len(), 2);
217
218        builder.append_hana_value(&HdbValue::INT(3)).unwrap();
219        assert_eq!(builder.len(), 3);
220    }
221
222    #[test]
223    fn test_len_includes_nulls() {
224        let mut builder = Int32BuilderWrapper::new(10);
225        builder.append_hana_value(&HdbValue::INT(1)).unwrap();
226        builder.append_null();
227        builder.append_hana_value(&HdbValue::INT(3)).unwrap();
228        assert_eq!(builder.len(), 3);
229    }
230
231    #[test]
232    fn test_len_resets_after_finish() {
233        let mut builder = Int32BuilderWrapper::new(10);
234        builder.append_hana_value(&HdbValue::INT(1)).unwrap();
235        builder.append_hana_value(&HdbValue::INT(2)).unwrap();
236        assert_eq!(builder.len(), 2);
237
238        let _ = builder.finish();
239        assert_eq!(builder.len(), 0);
240    }
241
242    // ═══════════════════════════════════════════════════════════════════════════
243    // finish Tests
244    // ═══════════════════════════════════════════════════════════════════════════
245
246    #[test]
247    fn test_finish_returns_array_with_correct_length() {
248        let mut builder = Int32BuilderWrapper::new(10);
249        builder.append_hana_value(&HdbValue::INT(1)).unwrap();
250        builder.append_hana_value(&HdbValue::INT(2)).unwrap();
251        builder.append_hana_value(&HdbValue::INT(3)).unwrap();
252
253        let array = builder.finish();
254        assert_eq!(array.len(), 3);
255    }
256
257    #[test]
258    fn test_finish_empty_builder_returns_empty_array() {
259        let mut builder = Int32BuilderWrapper::new(10);
260        let array = builder.finish();
261        assert_eq!(array.len(), 0);
262    }
263
264    #[test]
265    fn test_finish_multiple_times() {
266        let mut builder = Int32BuilderWrapper::new(10);
267
268        builder.append_hana_value(&HdbValue::INT(1)).unwrap();
269        let array1 = builder.finish();
270        assert_eq!(array1.len(), 1);
271
272        builder.append_hana_value(&HdbValue::INT(2)).unwrap();
273        builder.append_hana_value(&HdbValue::INT(3)).unwrap();
274        let array2 = builder.finish();
275        assert_eq!(array2.len(), 2);
276    }
277}