odbc_api/buffers/
text_column.rs

1use crate::{
2    DataType, Error,
3    buffers::Resize,
4    columnar_bulk_inserter::BoundInputSlice,
5    error::TooLargeBufferSize,
6    handles::{
7        ASSUMED_MAX_LENGTH_OF_W_VARCHAR, CData, CDataMut, HasDataType, Statement, StatementRef,
8    },
9};
10
11use super::{ColumnBuffer, Indicator};
12
13use log::debug;
14use odbc_sys::{CDataType, NULL_DATA};
15use std::{cmp::min, ffi::c_void, mem::size_of, num::NonZeroUsize, panic};
16use widestring::U16Str;
17
18/// A column buffer for character data. The actual encoding used may depend on your system locale.
19pub type CharColumn = TextColumn<u8>;
20
21/// This buffer uses wide characters which implies UTF-16 encoding. UTF-8 encoding is preferable for
22/// most applications, but contrary to its sibling [`crate::buffers::CharColumn`] this buffer type's
23/// implied encoding does not depend on the system locale.
24pub type WCharColumn = TextColumn<u16>;
25
26/// A buffer intended to be bound to a column of a cursor. Elements of the buffer will contain a
27/// variable amount of characters up to a maximum string length. Since most SQL types have a string
28/// representation this buffer can be bound to a column of almost any type, ODBC driver and driver
29/// manager should take care of the conversion. Since elements of this type have variable length, an
30/// indicator buffer needs to be bound, whether the column is nullable or not.
31///
32/// Character type `C` is intended to be either `u8` or `u16`.
33#[derive(Debug)]
34pub struct TextColumn<C> {
35    /// Maximum text length without terminating zero.
36    max_str_len: usize,
37    /// All the characters of all the elements in the buffer. The first character of the n-th
38    /// element is at index `n * (max_str_len + 1)`.
39    values: Vec<C>,
40    /// Elements in this buffer are either `NULL_DATA` or hold the length of the element in value
41    /// with the same index. Please note that this value may be larger than `max_str_len` if the
42    /// text has been truncated.
43    indicators: Vec<isize>,
44}
45
46impl<C> TextColumn<C> {
47    /// This will allocate a value and indicator buffer for `batch_size` elements. Each value may
48    /// have a maximum length of `max_str_len`. This implies that `max_str_len` is increased by
49    /// one in order to make space for the null terminating zero at the end of strings. Uses a
50    /// fallible allocation for creating the buffer. In applications often the `max_str_len` size
51    /// of the buffer, might be directly inspired by the maximum size of the type, as reported, by
52    /// ODBC. Which might get exceedingly large for types like VARCHAR(MAX)
53    pub fn try_new(batch_size: usize, max_str_len: usize) -> Result<Self, TooLargeBufferSize>
54    where
55        C: Default + Copy,
56    {
57        // Element size is +1 to account for terminating zero
58        let element_size = max_str_len + 1;
59        let len = element_size * batch_size;
60        let mut values = Vec::new();
61        values
62            .try_reserve_exact(len)
63            .map_err(|_| TooLargeBufferSize {
64                num_elements: batch_size,
65                // We want the element size in bytes
66                element_size: element_size * size_of::<C>(),
67            })?;
68        values.resize(len, C::default());
69        Ok(TextColumn {
70            max_str_len,
71            values,
72            indicators: vec![0; batch_size],
73        })
74    }
75
76    /// This will allocate a value and indicator buffer for `batch_size` elements. Each value may
77    /// have a maximum length of `max_str_len`. This implies that `max_str_len` is increased by
78    /// one in order to make space for the null terminating zero at the end of strings. All
79    /// indicators are set to [`crate::sys::NULL_DATA`] by default.
80    pub fn new(batch_size: usize, max_str_len: usize) -> Self
81    where
82        C: Default + Copy,
83    {
84        // Element size is +1 to account for terminating zero
85        let element_size = max_str_len + 1;
86        let len = element_size * batch_size;
87        let mut values = Vec::new();
88        values.reserve_exact(len);
89        values.resize(len, C::default());
90        TextColumn {
91            max_str_len,
92            values,
93            indicators: vec![NULL_DATA; batch_size],
94        }
95    }
96
97    /// Bytes of string at the specified position. Includes interior nuls, but excludes the
98    /// terminating nul.
99    ///
100    /// The column buffer does not know how many elements were in the last row group, and therefore
101    /// can not guarantee the accessed element to be valid and in a defined state. It also can not
102    /// panic on accessing an undefined element. It will panic however if `row_index` is larger or
103    /// equal to the maximum number of elements in the buffer.
104    pub fn value_at(&self, row_index: usize) -> Option<&[C]> {
105        self.content_length_at(row_index).map(|length| {
106            let offset = row_index * (self.max_str_len + 1);
107            &self.values[offset..offset + length]
108        })
109    }
110
111    /// Maximum length of elements
112    pub fn max_len(&self) -> usize {
113        self.max_str_len
114    }
115
116    /// Indicator value at the specified position. Useful to detect truncation of data.
117    ///
118    /// The column buffer does not know how many elements were in the last row group, and therefore
119    /// can not guarantee the accessed element to be valid and in a defined state. It also can not
120    /// panic on accessing an undefined element. It will panic however if `row_index` is larger or
121    /// equal to the maximum number of elements in the buffer.
122    pub fn indicator_at(&self, row_index: usize) -> Indicator {
123        Indicator::from_isize(self.indicators[row_index])
124    }
125
126    /// Length of value at the specified position. This is different from an indicator as it refers
127    /// to the length of the value in the buffer, not to the length of the value in the datasource.
128    /// The two things are different for truncated values.
129    pub fn content_length_at(&self, row_index: usize) -> Option<usize> {
130        match self.indicator_at(row_index) {
131            Indicator::Null => None,
132            // Seen no total in the wild then binding shorter buffer to fixed sized CHAR in MSSQL.
133            Indicator::NoTotal => Some(self.max_str_len),
134            Indicator::Length(length_in_bytes) => {
135                let length_in_chars = length_in_bytes / size_of::<C>();
136                let length = min(self.max_str_len, length_in_chars);
137                Some(length)
138            }
139        }
140    }
141
142    /// Finds an indiactor larger than the maximum element size in the range [0, num_rows).
143    ///
144    /// After fetching data we may want to know if any value has been truncated due to the buffer
145    /// not being able to hold elements of that size. This method checks the indicator buffer
146    /// element wise.
147    pub fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
148        let max_bin_length = self.max_str_len * size_of::<C>();
149        self.indicators
150            .iter()
151            .copied()
152            .take(num_rows)
153            .find_map(|indicator| {
154                let indicator = Indicator::from_isize(indicator);
155                indicator.is_truncated(max_bin_length).then_some(indicator)
156            })
157    }
158
159    /// Changes the maximum string length the buffer can hold. This operation is useful if you find
160    /// an unexpected large input string during insertion.
161    ///
162    /// This is however costly, as not only does the new buffer have to be allocated, but all values
163    /// have to copied from the old to the new buffer.
164    ///
165    /// This method could also be used to reduce the maximum string length, which would truncate
166    /// strings in the process.
167    ///
168    /// This method does not adjust indicator buffers as these might hold values larger than the
169    /// maximum string length.
170    ///
171    /// # Parameters
172    ///
173    /// * `new_max_str_len`: New maximum string length without terminating zero.
174    /// * `num_rows`: Number of valid rows currently stored in this buffer.
175    pub fn resize_max_str(&mut self, new_max_str_len: usize, num_rows: usize)
176    where
177        C: Default + Copy,
178    {
179        debug!(
180            "Rebinding text column buffer with {} elements. Maximum string length {} => {}",
181            num_rows, self.max_str_len, new_max_str_len
182        );
183
184        let batch_size = self.indicators.len();
185        // Allocate a new buffer large enough to hold a batch of strings with maximum length.
186        let mut new_values = vec![C::default(); (new_max_str_len + 1) * batch_size];
187        // Copy values from old to new buffer.
188        let max_copy_length = min(self.max_str_len, new_max_str_len);
189        for ((&indicator, old_value), new_value) in self
190            .indicators
191            .iter()
192            .zip(self.values.chunks_exact_mut(self.max_str_len + 1))
193            .zip(new_values.chunks_exact_mut(new_max_str_len + 1))
194            .take(num_rows)
195        {
196            match Indicator::from_isize(indicator) {
197                Indicator::Null => (),
198                Indicator::NoTotal => {
199                    // There is no good choice here in case we are expanding the buffer. Since
200                    // NO_TOTAL indicates that we use the entire buffer, but in truth it would now
201                    // be padded with 0. I currently cannot think of any use case there it would
202                    // matter.
203                    new_value[..max_copy_length].clone_from_slice(&old_value[..max_copy_length]);
204                }
205                Indicator::Length(num_bytes_len) => {
206                    let num_bytes_to_copy = min(num_bytes_len / size_of::<C>(), max_copy_length);
207                    new_value[..num_bytes_to_copy].copy_from_slice(&old_value[..num_bytes_to_copy]);
208                }
209            }
210        }
211        self.values = new_values;
212        self.max_str_len = new_max_str_len;
213    }
214
215    /// Sets the value of the buffer at index at Null or the specified binary Text. This method will
216    /// panic on out of bounds index, or if input holds a text which is larger than the maximum
217    /// allowed element length. `input` must be specified without the terminating zero.
218    pub fn set_value(&mut self, index: usize, input: Option<&[C]>)
219    where
220        C: Default + Copy,
221    {
222        if let Some(input) = input {
223            self.set_mut(index, input.len()).copy_from_slice(input);
224        } else {
225            self.indicators[index] = NULL_DATA;
226        }
227    }
228
229    /// Can be used to set a value at a specific row index without performing a memcopy on an input
230    /// slice and instead provides direct access to the underlying buffer.
231    ///
232    /// In situations there the memcopy can not be avoided anyway [`Self::set_value`] is likely to
233    /// be more convenient. This method is very useful if you want to `write!` a string value to the
234    /// buffer and the binary (**!**) length of the formatted string is known upfront.
235    ///
236    /// # Example: Write timestamp to text column.
237    ///
238    /// ```
239    /// use odbc_api::buffers::TextColumn;
240    /// use std::io::Write;
241    ///
242    /// /// Writes times formatted as hh::mm::ss.fff
243    /// fn write_time(
244    ///     col: &mut TextColumn<u8>,
245    ///     index: usize,
246    ///     hours: u8,
247    ///     minutes: u8,
248    ///     seconds: u8,
249    ///     milliseconds: u16)
250    /// {
251    ///     write!(
252    ///         col.set_mut(index, 12),
253    ///         "{:02}:{:02}:{:02}.{:03}",
254    ///         hours, minutes, seconds, milliseconds
255    ///     ).unwrap();
256    /// }
257    /// ```
258    pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C]
259    where
260        C: Default,
261    {
262        if length > self.max_str_len {
263            panic!(
264                "Tried to insert a value into a text buffer which is larger than the maximum \
265                allowed string length for the buffer."
266            );
267        }
268        self.indicators[index] = (length * size_of::<C>()).try_into().unwrap();
269        let start = (self.max_str_len + 1) * index;
270        let end = start + length;
271        // Let's insert a terminating zero at the end to be on the safe side, in case the ODBC
272        // driver would not care about the value in the index buffer and only look for the
273        // terminating zero.
274        self.values[end] = C::default();
275        &mut self.values[start..end]
276    }
277
278    /// Fills the column with NULL, between From and To
279    pub fn fill_null(&mut self, from: usize, to: usize) {
280        for index in from..to {
281            self.indicators[index] = NULL_DATA;
282        }
283    }
284
285    /// Provides access to the raw underlying value buffer. Normal applications should have little
286    /// reason to call this method. Yet it may be useful for writing bindings which copy directly
287    /// from the ODBC in memory representation into other kinds of buffers.
288    ///
289    /// The buffer contains the bytes for every non null valid element, padded to the maximum string
290    /// length. The content of the padding bytes is undefined. Usually ODBC drivers write a
291    /// terminating zero at the end of each string. For the actual value length call
292    /// [`Self::content_length_at`]. Any element starts at index * ([`Self::max_len`] + 1).
293    pub fn raw_value_buffer(&self, num_valid_rows: usize) -> &[C] {
294        &self.values[..(self.max_str_len + 1) * num_valid_rows]
295    }
296
297    /// The maximum number of rows the TextColumn can hold.
298    pub fn row_capacity(&self) -> usize {
299        self.values.len()
300    }
301}
302
303impl WCharColumn {
304    /// The string slice at the specified position as `U16Str`. Includes interior nuls, but excludes
305    /// the terminating nul.
306    ///
307    /// # Safety
308    ///
309    /// The column buffer does not know how many elements were in the last row group, and therefore
310    /// can not guarantee the accessed element to be valid and in a defined state. It also can not
311    /// panic on accessing an undefined element. It will panic however if `row_index` is larger or
312    /// equal to the maximum number of elements in the buffer.
313    pub unsafe fn ustr_at(&self, row_index: usize) -> Option<&U16Str> {
314        self.value_at(row_index).map(U16Str::from_slice)
315    }
316}
317
318unsafe impl<C: 'static> ColumnBuffer for TextColumn<C>
319where
320    TextColumn<C>: CDataMut + HasDataType,
321{
322    type View<'a> = TextColumnView<'a, C>;
323
324    fn view(&self, valid_rows: usize) -> TextColumnView<'_, C> {
325        TextColumnView {
326            num_rows: valid_rows,
327            col: self,
328        }
329    }
330
331    /// Maximum number of text strings this column may hold.
332    fn capacity(&self) -> usize {
333        self.indicators.len()
334    }
335
336    fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
337        let max_bin_length = self.max_str_len * size_of::<C>();
338        self.indicators
339            .iter()
340            .copied()
341            .take(num_rows)
342            .find_map(|indicator| {
343                let indicator = Indicator::from_isize(indicator);
344                indicator.is_truncated(max_bin_length).then_some(indicator)
345            })
346    }
347}
348
349/// Allows read-only access to the valid part of a text column.
350///
351/// You may ask, why is this type required, should we not just be able to use `&TextColumn`? The
352/// problem with `TextColumn` is, that it is a buffer, but it has no idea how many of its members
353/// are actually valid, and have been returned with the last row group of the result set. That
354/// number is maintained on the level of the entire column buffer. So a text column knows the number
355/// of valid rows, in addition to holding a reference to the buffer, in order to guarantee, that
356/// every element accessed through it, is valid.
357#[derive(Debug, Clone, Copy)]
358pub struct TextColumnView<'c, C> {
359    num_rows: usize,
360    col: &'c TextColumn<C>,
361}
362
363impl<'c, C> TextColumnView<'c, C> {
364    /// The number of valid elements in the text column.
365    pub fn len(&self) -> usize {
366        self.num_rows
367    }
368
369    /// True if, and only if there are no valid rows in the column buffer.
370    pub fn is_empty(&self) -> bool {
371        self.num_rows == 0
372    }
373
374    /// Slice of text at the specified row index without terminating zero. `None` if the value is
375    /// `NULL`. This method will panic if the index is larger than the number of valid rows in the
376    /// view as returned by [`Self::len`].
377    pub fn get(&self, index: usize) -> Option<&'c [C]> {
378        self.col.value_at(index)
379    }
380
381    /// Iterator over the valid elements of the text buffer
382    pub fn iter(&self) -> TextColumnIt<'c, C> {
383        TextColumnIt {
384            pos: 0,
385            num_rows: self.num_rows,
386            col: self.col,
387        }
388    }
389
390    /// Length of value at the specified position. This is different from an indicator as it refers
391    /// to the length of the value in the buffer, not to the length of the value in the datasource.
392    /// The two things are different for truncated values.
393    pub fn content_length_at(&self, row_index: usize) -> Option<usize> {
394        if row_index >= self.num_rows {
395            panic!("Row index points beyond the range of valid values.")
396        }
397        self.col.content_length_at(row_index)
398    }
399
400    /// Provides access to the raw underlying value buffer. Normal applications should have little
401    /// reason to call this method. Yet it may be useful for writing bindings which copy directly
402    /// from the ODBC in memory representation into other kinds of buffers.
403    ///
404    /// The buffer contains the bytes for every non null valid element, padded to the maximum string
405    /// length. The content of the padding bytes is undefined. Usually ODBC drivers write a
406    /// terminating zero at the end of each string. For the actual value length call
407    /// [`Self::content_length_at`]. Any element starts at index * ([`Self::max_len`] + 1).
408    pub fn raw_value_buffer(&self) -> &'c [C] {
409        self.col.raw_value_buffer(self.num_rows)
410    }
411
412    pub fn max_len(&self) -> usize {
413        self.col.max_len()
414    }
415
416    /// `Some` if any value is truncated.
417    ///
418    /// After fetching data we may want to know if any value has been truncated due to the buffer
419    /// not being able to hold elements of that size. This method checks the indicator buffer
420    /// element wise.
421    pub fn has_truncated_values(&self) -> Option<Indicator> {
422        self.col.has_truncated_values(self.num_rows)
423    }
424}
425
426unsafe impl<'a, C: 'static> BoundInputSlice<'a> for TextColumn<C> {
427    type SliceMut = TextColumnSliceMut<'a, C>;
428
429    unsafe fn as_view_mut(
430        &'a mut self,
431        parameter_index: u16,
432        stmt: StatementRef<'a>,
433    ) -> Self::SliceMut {
434        TextColumnSliceMut {
435            column: self,
436            stmt,
437            parameter_index,
438        }
439    }
440}
441
442/// A view to a mutable array parameter text buffer, which allows for filling the buffer with
443/// values.
444pub struct TextColumnSliceMut<'a, C> {
445    column: &'a mut TextColumn<C>,
446    // Needed to rebind the column in case of resize
447    stmt: StatementRef<'a>,
448    // Also needed to rebind the column in case of resize
449    parameter_index: u16,
450}
451
452impl<C> TextColumnSliceMut<'_, C>
453where
454    C: Default + Copy + Send,
455{
456    /// Sets the value of the buffer at index at Null or the specified binary Text. This method will
457    /// panic on out of bounds index, or if input holds a text which is larger than the maximum
458    /// allowed element length. `element` must be specified without the terminating zero.
459    pub fn set_cell(&mut self, row_index: usize, element: Option<&[C]>) {
460        self.column.set_value(row_index, element)
461    }
462
463    /// Ensures that the buffer is large enough to hold elements of `element_length`. Does nothing
464    /// if the buffer is already large enough. Otherwise it will reallocate and rebind the buffer.
465    /// The first `num_rows_to_copy` will be copied from the old value buffer to the new
466    /// one. This makes this an extremely expensive operation.
467    pub fn ensure_max_element_length(
468        &mut self,
469        element_length: usize,
470        num_rows_to_copy: usize,
471    ) -> Result<(), Error>
472    where
473        TextColumn<C>: HasDataType + CData,
474    {
475        // Column buffer is not large enough to hold the element. We must allocate a larger buffer
476        // in order to hold it. This invalidates the pointers previously bound to the statement. So
477        // we rebind them.
478        if element_length > self.column.max_len() {
479            let new_max_str_len = element_length;
480            self.column
481                .resize_max_str(new_max_str_len, num_rows_to_copy);
482            unsafe {
483                self.stmt
484                    .bind_input_parameter(self.parameter_index, self.column)
485                    .into_result(&self.stmt)?
486            }
487        }
488        Ok(())
489    }
490
491    /// Can be used to set a value at a specific row index without performing a memcopy on an input
492    /// slice and instead provides direct access to the underlying buffer.
493    ///
494    /// In situations there the memcopy can not be avoided anyway [`Self::set_cell`] is likely to
495    /// be more convenient. This method is very useful if you want to `write!` a string value to the
496    /// buffer and the binary (**!**) length of the formatted string is known upfront.
497    ///
498    /// # Example: Write timestamp to text column.
499    ///
500    /// ```
501    /// use odbc_api::buffers::TextColumnSliceMut;
502    /// use std::io::Write;
503    ///
504    /// /// Writes times formatted as hh::mm::ss.fff
505    /// fn write_time(
506    ///     col: &mut TextColumnSliceMut<u8>,
507    ///     index: usize,
508    ///     hours: u8,
509    ///     minutes: u8,
510    ///     seconds: u8,
511    ///     milliseconds: u16)
512    /// {
513    ///     write!(
514    ///         col.set_mut(index, 12),
515    ///         "{:02}:{:02}:{:02}.{:03}",
516    ///         hours, minutes, seconds, milliseconds
517    ///     ).unwrap();
518    /// }
519    /// ```
520    pub fn set_mut(&mut self, index: usize, length: usize) -> &mut [C] {
521        self.column.set_mut(index, length)
522    }
523}
524
525/// Iterator over a text column. See [`TextColumnView::iter`]
526#[derive(Debug)]
527pub struct TextColumnIt<'c, C> {
528    pos: usize,
529    num_rows: usize,
530    col: &'c TextColumn<C>,
531}
532
533impl<'c, C> TextColumnIt<'c, C> {
534    fn next_impl(&mut self) -> Option<Option<&'c [C]>> {
535        if self.pos == self.num_rows {
536            None
537        } else {
538            let ret = Some(self.col.value_at(self.pos));
539            self.pos += 1;
540            ret
541        }
542    }
543}
544
545impl<'c> Iterator for TextColumnIt<'c, u8> {
546    type Item = Option<&'c [u8]>;
547
548    fn next(&mut self) -> Option<Self::Item> {
549        self.next_impl()
550    }
551
552    fn size_hint(&self) -> (usize, Option<usize>) {
553        let len = self.num_rows - self.pos;
554        (len, Some(len))
555    }
556}
557
558impl ExactSizeIterator for TextColumnIt<'_, u8> {}
559
560impl<'c> Iterator for TextColumnIt<'c, u16> {
561    type Item = Option<&'c U16Str>;
562
563    fn next(&mut self) -> Option<Self::Item> {
564        self.next_impl().map(|opt| opt.map(U16Str::from_slice))
565    }
566
567    fn size_hint(&self) -> (usize, Option<usize>) {
568        let len = self.num_rows - self.pos;
569        (len, Some(len))
570    }
571}
572
573impl ExactSizeIterator for TextColumnIt<'_, u16> {}
574
575unsafe impl CData for CharColumn {
576    fn cdata_type(&self) -> CDataType {
577        CDataType::Char
578    }
579
580    fn indicator_ptr(&self) -> *const isize {
581        self.indicators.as_ptr()
582    }
583
584    fn value_ptr(&self) -> *const c_void {
585        self.values.as_ptr() as *const c_void
586    }
587
588    fn buffer_length(&self) -> isize {
589        (self.max_str_len + 1).try_into().unwrap()
590    }
591}
592
593unsafe impl CDataMut for CharColumn {
594    fn mut_indicator_ptr(&mut self) -> *mut isize {
595        self.indicators.as_mut_ptr()
596    }
597
598    fn mut_value_ptr(&mut self) -> *mut c_void {
599        self.values.as_mut_ptr() as *mut c_void
600    }
601}
602
603impl HasDataType for CharColumn {
604    fn data_type(&self) -> DataType {
605        DataType::Varchar {
606            length: NonZeroUsize::new(self.max_str_len),
607        }
608    }
609}
610
611unsafe impl CData for WCharColumn {
612    fn cdata_type(&self) -> CDataType {
613        CDataType::WChar
614    }
615
616    fn indicator_ptr(&self) -> *const isize {
617        self.indicators.as_ptr()
618    }
619
620    fn value_ptr(&self) -> *const c_void {
621        self.values.as_ptr() as *const c_void
622    }
623
624    fn buffer_length(&self) -> isize {
625        ((self.max_str_len + 1) * 2).try_into().unwrap()
626    }
627}
628
629unsafe impl CDataMut for WCharColumn {
630    fn mut_indicator_ptr(&mut self) -> *mut isize {
631        self.indicators.as_mut_ptr()
632    }
633
634    fn mut_value_ptr(&mut self) -> *mut c_void {
635        self.values.as_mut_ptr() as *mut c_void
636    }
637}
638
639impl HasDataType for WCharColumn {
640    fn data_type(&self) -> DataType {
641        if self.max_str_len <= ASSUMED_MAX_LENGTH_OF_W_VARCHAR {
642            DataType::WVarchar {
643                length: NonZeroUsize::new(self.max_str_len),
644            }
645        } else {
646            DataType::WLongVarchar {
647                length: NonZeroUsize::new(self.max_str_len),
648            }
649        }
650    }
651}
652
653impl<C> Resize for TextColumn<C>
654where
655    C: Clone + Default,
656{
657    fn resize(&mut self, new_capacity: usize) {
658        self.values
659            .resize((self.max_str_len + 1) * new_capacity, C::default());
660        self.indicators.resize(new_capacity, NULL_DATA);
661    }
662}
663
664#[cfg(test)]
665mod test {
666    use crate::buffers::{Resize, TextColumn};
667
668    #[test]
669    fn resize_text_column_buffer() {
670        // Given a text column buffer with two elements
671        let mut col = TextColumn::<u8>::new(2, 10);
672        col.set_value(0, Some(b"Hello"));
673        col.set_value(1, Some(b"World"));
674
675        // When we resize it to hold 3 elements
676        col.resize(3);
677
678        // Then the first two elements are still there, and the third is None
679        assert_eq!(col.value_at(0), Some(b"Hello".as_ref()));
680        assert_eq!(col.value_at(1), Some(b"World".as_ref()));
681        assert_eq!(col.value_at(2), None);
682    }
683}