Skip to main content

alimentar/tui/
adapter.rs

1//! Dataset adapter for TUI viewing
2//!
3//! Provides uniform access to Arrow datasets for TUI rendering.
4//! Supports both in-memory and streaming modes for memory efficiency.
5
6use std::sync::Arc;
7
8use arrow::{
9    array::RecordBatch,
10    datatypes::{Schema, SchemaRef},
11};
12use unicode_width::UnicodeWidthStr;
13
14use super::{error::TuiResult, format::format_array_value};
15use crate::{dataset::ArrowDataset, Dataset};
16
17/// Threshold for switching from in-memory to streaming mode (rows)
18const STREAMING_THRESHOLD: usize = 100_000;
19
20/// Adapter providing uniform access to Arrow datasets for TUI rendering
21///
22/// Supports two modes:
23/// - `InMemory`: All batches loaded upfront, fast random access
24/// - `Streaming`: Lazy batch loading for large datasets (OOM prevention)
25///
26/// # Example
27///
28/// ```ignore
29/// use alimentar::tui::DatasetAdapter;
30/// use alimentar::ArrowDataset;
31///
32/// let dataset = ArrowDataset::from_parquet("data.parquet")?;
33/// let adapter = DatasetAdapter::from_dataset(&dataset)?;
34///
35/// println!("Rows: {}", adapter.row_count());
36/// println!("Columns: {}", adapter.column_count());
37///
38/// if let Some(value) = adapter.get_cell(0, 0)? {
39///     println!("First cell: {}", value);
40/// }
41/// ```
42#[derive(Debug, Clone)]
43pub enum DatasetAdapter {
44    /// All batches loaded in memory - fast random access
45    InMemory(InMemoryAdapter),
46    /// Lazy batch loading for large datasets
47    Streaming(StreamingAdapter),
48}
49
50/// In-memory adapter with all batches loaded
51#[derive(Debug, Clone)]
52pub struct InMemoryAdapter {
53    /// Record batches containing the data
54    batches: Vec<RecordBatch>,
55    /// Cached schema reference
56    schema: SchemaRef,
57    /// Cached total row count
58    total_rows: usize,
59    /// Cached column count
60    column_count: usize,
61    /// Cumulative row offsets for batch lookup
62    batch_offsets: Vec<usize>,
63}
64
65/// Streaming adapter for lazy batch loading (stub implementation)
66///
67/// This adapter is designed for datasets too large to fit in memory.
68/// Batches are loaded on-demand and evicted when not needed.
69#[derive(Debug, Clone)]
70pub struct StreamingAdapter {
71    /// Cached schema reference
72    schema: SchemaRef,
73    /// Total row count (known from metadata)
74    total_rows: usize,
75    /// Column count
76    column_count: usize,
77    /// Currently loaded batches (LRU cache would go here)
78    loaded_batches: Vec<RecordBatch>,
79    /// Cumulative row offsets
80    batch_offsets: Vec<usize>,
81}
82
83impl DatasetAdapter {
84    /// Create adapter from an `ArrowDataset`
85    ///
86    /// Automatically selects InMemory or Streaming mode based on dataset size.
87    ///
88    /// # Arguments
89    /// * `dataset` - The Arrow dataset to adapt
90    ///
91    /// # Returns
92    /// A new adapter, or error if the dataset has no schema
93    pub fn from_dataset(dataset: &ArrowDataset) -> TuiResult<Self> {
94        let schema = dataset.schema();
95        let batches: Vec<_> = dataset.iter().collect();
96        let total_rows: usize = batches.iter().map(|b| b.num_rows()).sum();
97
98        // Choose mode based on dataset size (F103)
99        if total_rows > STREAMING_THRESHOLD {
100            Self::streaming_from_batches(batches, schema)
101        } else {
102            Self::in_memory_from_batches(batches, schema)
103        }
104    }
105
106    /// Create in-memory adapter from record batches and schema
107    pub fn from_batches(batches: Vec<RecordBatch>, schema: SchemaRef) -> TuiResult<Self> {
108        Self::in_memory_from_batches(batches, schema)
109    }
110
111    /// Create in-memory adapter explicitly
112    pub fn in_memory_from_batches(batches: Vec<RecordBatch>, schema: SchemaRef) -> TuiResult<Self> {
113        Ok(Self::InMemory(InMemoryAdapter::new(batches, schema)?))
114    }
115
116    /// Create streaming adapter explicitly
117    pub fn streaming_from_batches(batches: Vec<RecordBatch>, schema: SchemaRef) -> TuiResult<Self> {
118        Ok(Self::Streaming(StreamingAdapter::new(batches, schema)?))
119    }
120
121    /// Create an empty adapter
122    pub fn empty() -> Self {
123        Self::InMemory(InMemoryAdapter::empty())
124    }
125
126    /// Get the schema reference
127    #[inline]
128    pub fn schema(&self) -> &SchemaRef {
129        match self {
130            Self::InMemory(a) => a.schema(),
131            Self::Streaming(a) => a.schema(),
132        }
133    }
134
135    /// Get the total row count
136    #[inline]
137    pub fn row_count(&self) -> usize {
138        match self {
139            Self::InMemory(a) => a.row_count(),
140            Self::Streaming(a) => a.row_count(),
141        }
142    }
143
144    /// Get the column count
145    #[inline]
146    pub fn column_count(&self) -> usize {
147        match self {
148            Self::InMemory(a) => a.column_count(),
149            Self::Streaming(a) => a.column_count(),
150        }
151    }
152
153    /// Check if the dataset is empty
154    #[inline]
155    pub fn is_empty(&self) -> bool {
156        self.row_count() == 0
157    }
158
159    /// Check if this adapter is in streaming mode
160    #[inline]
161    pub fn is_streaming(&self) -> bool {
162        matches!(self, Self::Streaming(_))
163    }
164
165    /// Get a cell value as a formatted string
166    pub fn get_cell(&self, row: usize, col: usize) -> TuiResult<Option<String>> {
167        match self {
168            Self::InMemory(a) => a.get_cell(row, col),
169            Self::Streaming(a) => a.get_cell(row, col),
170        }
171    }
172
173    /// Get a field name by column index
174    pub fn field_name(&self, col: usize) -> Option<&str> {
175        match self {
176            Self::InMemory(a) => a.field_name(col),
177            Self::Streaming(a) => a.field_name(col),
178        }
179    }
180
181    /// Get a field data type description by column index
182    pub fn field_type(&self, col: usize) -> Option<String> {
183        match self {
184            Self::InMemory(a) => a.field_type(col),
185            Self::Streaming(a) => a.field_type(col),
186        }
187    }
188
189    /// Check if a field is nullable
190    pub fn field_nullable(&self, col: usize) -> Option<bool> {
191        match self {
192            Self::InMemory(a) => a.field_nullable(col),
193            Self::Streaming(a) => a.field_nullable(col),
194        }
195    }
196
197    /// Calculate optimal column widths for display
198    ///
199    /// Uses `unicode-width` for correct visual width calculation.
200    pub fn calculate_column_widths(&self, max_width: u16, sample_rows: usize) -> Vec<u16> {
201        match self {
202            Self::InMemory(a) => a.calculate_column_widths(max_width, sample_rows),
203            Self::Streaming(a) => a.calculate_column_widths(max_width, sample_rows),
204        }
205    }
206
207    /// Get all field names as a vector
208    pub fn field_names(&self) -> Vec<&str> {
209        match self {
210            Self::InMemory(a) => a.field_names(),
211            Self::Streaming(a) => a.field_names(),
212        }
213    }
214
215    /// Locate a row within the batch structure
216    pub fn locate_row(&self, global_row: usize) -> Option<(usize, usize)> {
217        match self {
218            Self::InMemory(a) => a.locate_row(global_row),
219            Self::Streaming(a) => a.locate_row(global_row),
220        }
221    }
222
223    /// Search for a substring in string columns, returning first matching row
224    ///
225    /// Linear scan implementation suitable for <100k rows (F101).
226    pub fn search(&self, query: &str) -> Option<usize> {
227        if query.is_empty() {
228            return None;
229        }
230        let query_lower = query.to_lowercase();
231
232        for row in 0..self.row_count() {
233            for col in 0..self.column_count() {
234                if let Ok(Some(value)) = self.get_cell(row, col) {
235                    if value.to_lowercase().contains(&query_lower) {
236                        return Some(row);
237                    }
238                }
239            }
240        }
241        None
242    }
243
244    /// Search continuing from a given row
245    pub fn search_from(&self, query: &str, start_row: usize) -> Option<usize> {
246        if query.is_empty() {
247            return None;
248        }
249        let query_lower = query.to_lowercase();
250
251        (start_row..self.row_count())
252            .find(|&row| self.row_contains(row, &query_lower))
253            // Wrap around to beginning
254            .or_else(|| (0..start_row).find(|&row| self.row_contains(row, &query_lower)))
255    }
256
257    /// Whether any cell in `row` contains `query_lower`, which must already be lower-cased.
258    fn row_contains(&self, row: usize, query_lower: &str) -> bool {
259        for col in 0..self.column_count() {
260            if let Ok(Some(value)) = self.get_cell(row, col) {
261                if value.to_lowercase().contains(query_lower) {
262                    return true;
263                }
264            }
265        }
266        false
267    }
268}
269
270impl InMemoryAdapter {
271    /// Create a new in-memory adapter
272    #[allow(clippy::unnecessary_wraps)]
273    pub fn new(batches: Vec<RecordBatch>, schema: SchemaRef) -> TuiResult<Self> {
274        let total_rows = batches.iter().map(|b| b.num_rows()).sum();
275        let column_count = schema.fields().len();
276
277        // Pre-compute batch offsets for O(log n) row lookup
278        let mut batch_offsets = Vec::with_capacity(batches.len() + 1);
279        batch_offsets.push(0);
280        let mut offset = 0;
281        for batch in &batches {
282            offset += batch.num_rows();
283            batch_offsets.push(offset);
284        }
285
286        Ok(Self {
287            batches,
288            schema,
289            total_rows,
290            column_count,
291            batch_offsets,
292        })
293    }
294
295    /// Create an empty adapter
296    pub fn empty() -> Self {
297        Self {
298            batches: Vec::new(),
299            schema: Arc::new(Schema::empty()),
300            total_rows: 0,
301            column_count: 0,
302            batch_offsets: vec![0],
303        }
304    }
305
306    #[inline]
307    pub fn schema(&self) -> &SchemaRef {
308        &self.schema
309    }
310
311    #[inline]
312    pub fn row_count(&self) -> usize {
313        self.total_rows
314    }
315
316    #[inline]
317    pub fn column_count(&self) -> usize {
318        self.column_count
319    }
320
321    pub fn get_cell(&self, row: usize, col: usize) -> TuiResult<Option<String>> {
322        if row >= self.total_rows || col >= self.column_count {
323            return Ok(None);
324        }
325
326        let Some((batch_idx, local_row)) = self.locate_row(row) else {
327            return Ok(None);
328        };
329
330        let Some(batch) = self.batches.get(batch_idx) else {
331            return Ok(None);
332        };
333
334        let array = batch.column(col);
335        format_array_value(array.as_ref(), local_row)
336    }
337
338    pub fn field_name(&self, col: usize) -> Option<&str> {
339        self.schema.fields().get(col).map(|f| f.name().as_str())
340    }
341
342    pub fn field_type(&self, col: usize) -> Option<String> {
343        self.schema
344            .fields()
345            .get(col)
346            .map(|f| format!("{:?}", f.data_type()))
347    }
348
349    pub fn field_nullable(&self, col: usize) -> Option<bool> {
350        self.schema.fields().get(col).map(|f| f.is_nullable())
351    }
352
353    pub fn locate_row(&self, global_row: usize) -> Option<(usize, usize)> {
354        if global_row >= self.total_rows {
355            return None;
356        }
357
358        let batch_idx = match self.batch_offsets.binary_search(&global_row) {
359            Ok(idx) => {
360                if idx < self.batches.len() {
361                    idx
362                } else {
363                    idx.saturating_sub(1)
364                }
365            }
366            Err(idx) => idx.saturating_sub(1),
367        };
368
369        let batch_start = self.batch_offsets.get(batch_idx).copied().unwrap_or(0);
370        let local_row = global_row.saturating_sub(batch_start);
371
372        Some((batch_idx, local_row))
373    }
374
375    /// Calculate column widths using unicode-width for correct visual width
376    pub fn calculate_column_widths(&self, max_width: u16, sample_rows: usize) -> Vec<u16> {
377        if self.column_count == 0 {
378            return Vec::new();
379        }
380
381        // Start with header widths (using unicode width)
382        let mut widths: Vec<u16> = self
383            .schema
384            .fields()
385            .iter()
386            .map(|f| {
387                let width = UnicodeWidthStr::width(f.name().as_str()).min(50);
388                u16::try_from(width).unwrap_or(u16::MAX)
389            })
390            .collect();
391
392        // Sample rows for content width
393        let sample_count = sample_rows.min(self.total_rows);
394        for row in 0..sample_count {
395            for col in 0..self.column_count {
396                if let Ok(Some(value)) = self.get_cell(row, col) {
397                    // Use unicode width for correct visual width
398                    let width = UnicodeWidthStr::width(value.as_str()).min(50);
399                    let width_u16 = u16::try_from(width).unwrap_or(u16::MAX);
400                    if let Some(w) = widths.get_mut(col) {
401                        *w = (*w).max(width_u16);
402                    }
403                }
404            }
405        }
406
407        // Ensure minimum width of 3 for each column
408        for w in &mut widths {
409            *w = (*w).max(3);
410        }
411
412        // Calculate separators and available space
413        let num_cols = u16::try_from(self.column_count).unwrap_or(u16::MAX);
414        let separators = num_cols.saturating_sub(1);
415        let available = max_width.saturating_sub(separators);
416
417        // Scale down if needed
418        let total: u16 = widths.iter().sum();
419        if total > available && available > 0 {
420            let scale = f64::from(available) / f64::from(total);
421            for w in &mut widths {
422                #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
423                let scaled = (f64::from(*w) * scale) as u16;
424                *w = scaled.max(3);
425            }
426        }
427
428        widths
429    }
430
431    pub fn field_names(&self) -> Vec<&str> {
432        self.schema
433            .fields()
434            .iter()
435            .map(|f| f.name().as_str())
436            .collect()
437    }
438}
439
440impl StreamingAdapter {
441    /// Create a new streaming adapter
442    ///
443    /// Note: This is currently a stub that loads all batches.
444    /// A full implementation would use an async iterator.
445    #[allow(clippy::unnecessary_wraps)]
446    pub fn new(batches: Vec<RecordBatch>, schema: SchemaRef) -> TuiResult<Self> {
447        let total_rows = batches.iter().map(|b| b.num_rows()).sum();
448        let column_count = schema.fields().len();
449
450        let mut batch_offsets = Vec::with_capacity(batches.len() + 1);
451        batch_offsets.push(0);
452        let mut offset = 0;
453        for batch in &batches {
454            offset += batch.num_rows();
455            batch_offsets.push(offset);
456        }
457
458        Ok(Self {
459            schema,
460            total_rows,
461            column_count,
462            loaded_batches: batches, // Deferred (PMAT-754): Replace with lazy loading
463            batch_offsets,
464        })
465    }
466
467    #[inline]
468    pub fn schema(&self) -> &SchemaRef {
469        &self.schema
470    }
471
472    #[inline]
473    pub fn row_count(&self) -> usize {
474        self.total_rows
475    }
476
477    #[inline]
478    pub fn column_count(&self) -> usize {
479        self.column_count
480    }
481
482    pub fn get_cell(&self, row: usize, col: usize) -> TuiResult<Option<String>> {
483        if row >= self.total_rows || col >= self.column_count {
484            return Ok(None);
485        }
486
487        let Some((batch_idx, local_row)) = self.locate_row(row) else {
488            return Ok(None);
489        };
490
491        let Some(batch) = self.loaded_batches.get(batch_idx) else {
492            return Ok(None);
493        };
494
495        let array = batch.column(col);
496        format_array_value(array.as_ref(), local_row)
497    }
498
499    pub fn field_name(&self, col: usize) -> Option<&str> {
500        self.schema.fields().get(col).map(|f| f.name().as_str())
501    }
502
503    pub fn field_type(&self, col: usize) -> Option<String> {
504        self.schema
505            .fields()
506            .get(col)
507            .map(|f| format!("{:?}", f.data_type()))
508    }
509
510    pub fn field_nullable(&self, col: usize) -> Option<bool> {
511        self.schema.fields().get(col).map(|f| f.is_nullable())
512    }
513
514    pub fn locate_row(&self, global_row: usize) -> Option<(usize, usize)> {
515        if global_row >= self.total_rows {
516            return None;
517        }
518
519        let batch_idx = match self.batch_offsets.binary_search(&global_row) {
520            Ok(idx) => {
521                if idx < self.loaded_batches.len() {
522                    idx
523                } else {
524                    idx.saturating_sub(1)
525                }
526            }
527            Err(idx) => idx.saturating_sub(1),
528        };
529
530        let batch_start = self.batch_offsets.get(batch_idx).copied().unwrap_or(0);
531        let local_row = global_row.saturating_sub(batch_start);
532
533        Some((batch_idx, local_row))
534    }
535
536    /// Calculate column widths using unicode-width
537    pub fn calculate_column_widths(&self, max_width: u16, sample_rows: usize) -> Vec<u16> {
538        if self.column_count == 0 {
539            return Vec::new();
540        }
541
542        let mut widths: Vec<u16> = self
543            .schema
544            .fields()
545            .iter()
546            .map(|f| {
547                let width = UnicodeWidthStr::width(f.name().as_str()).min(50);
548                u16::try_from(width).unwrap_or(u16::MAX)
549            })
550            .collect();
551
552        let sample_count = sample_rows.min(self.total_rows);
553        for row in 0..sample_count {
554            for col in 0..self.column_count {
555                if let Ok(Some(value)) = self.get_cell(row, col) {
556                    let width = UnicodeWidthStr::width(value.as_str()).min(50);
557                    let width_u16 = u16::try_from(width).unwrap_or(u16::MAX);
558                    if let Some(w) = widths.get_mut(col) {
559                        *w = (*w).max(width_u16);
560                    }
561                }
562            }
563        }
564
565        for w in &mut widths {
566            *w = (*w).max(3);
567        }
568
569        let num_cols = u16::try_from(self.column_count).unwrap_or(u16::MAX);
570        let separators = num_cols.saturating_sub(1);
571        let available = max_width.saturating_sub(separators);
572
573        let total: u16 = widths.iter().sum();
574        if total > available && available > 0 {
575            let scale = f64::from(available) / f64::from(total);
576            for w in &mut widths {
577                #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
578                let scaled = (f64::from(*w) * scale) as u16;
579                *w = scaled.max(3);
580            }
581        }
582
583        widths
584    }
585
586    pub fn field_names(&self) -> Vec<&str> {
587        self.schema
588            .fields()
589            .iter()
590            .map(|f| f.name().as_str())
591            .collect()
592    }
593}
594
595#[cfg(test)]
596mod tests {
597    use arrow::{
598        array::{Float32Array, Int32Array, StringArray},
599        datatypes::{DataType, Field},
600    };
601
602    use super::*;
603
604    fn create_test_schema() -> SchemaRef {
605        Arc::new(Schema::new(vec![
606            Field::new("id", DataType::Utf8, false),
607            Field::new("value", DataType::Int32, false),
608            Field::new("score", DataType::Float32, false),
609        ]))
610    }
611
612    fn create_test_batch(schema: &SchemaRef, start_id: i32, count: usize) -> RecordBatch {
613        let ids: Vec<String> = (0..count)
614            .map(|i| format!("id_{}", start_id + i as i32))
615            .collect();
616        let values: Vec<i32> = (0..count).map(|i| (start_id + i as i32) * 10).collect();
617        let scores: Vec<f32> = (0..count).map(|i| (i as f32) * 0.1).collect();
618
619        RecordBatch::try_new(
620            schema.clone(),
621            vec![
622                Arc::new(StringArray::from(ids)),
623                Arc::new(Int32Array::from(values)),
624                Arc::new(Float32Array::from(scores)),
625            ],
626        )
627        .unwrap()
628    }
629
630    fn create_test_adapter() -> DatasetAdapter {
631        let schema = create_test_schema();
632        let batch1 = create_test_batch(&schema, 0, 5);
633        let batch2 = create_test_batch(&schema, 5, 5);
634        DatasetAdapter::from_batches(vec![batch1, batch2], schema).unwrap()
635    }
636
637    #[test]
638    fn f001_adapter_row_count() {
639        let adapter = create_test_adapter();
640        assert_eq!(adapter.row_count(), 10, "FALSIFIED: Expected 10 rows");
641    }
642
643    #[test]
644    fn f002_adapter_column_count() {
645        let adapter = create_test_adapter();
646        assert_eq!(
647            adapter.column_count(),
648            3,
649            "FALSIFIED: Expected 3 columns (id, value, score)"
650        );
651    }
652
653    #[test]
654    fn f003_adapter_schema_o1() {
655        let adapter = create_test_adapter();
656        let schema = adapter.schema();
657        assert_eq!(schema.fields().len(), 3);
658    }
659
660    #[test]
661    fn f004_adapter_get_cell_first_batch() {
662        let adapter = create_test_adapter();
663        let cell = adapter.get_cell(0, 0).unwrap();
664        assert!(cell.is_some(), "FALSIFIED: Cell should exist");
665        assert_eq!(cell.unwrap(), "id_0");
666    }
667
668    #[test]
669    fn f005_adapter_get_cell_second_batch() {
670        let adapter = create_test_adapter();
671        let cell = adapter.get_cell(5, 0).unwrap();
672        assert!(cell.is_some(), "FALSIFIED: Cell should exist");
673        assert_eq!(cell.unwrap(), "id_5");
674    }
675
676    #[test]
677    fn f006_adapter_get_cell_row_out_of_bounds() {
678        let adapter = create_test_adapter();
679        let cell = adapter.get_cell(100, 0).unwrap();
680        assert!(
681            cell.is_none(),
682            "FALSIFIED: Out of bounds row should return None"
683        );
684    }
685
686    #[test]
687    fn f007_adapter_get_cell_col_out_of_bounds() {
688        let adapter = create_test_adapter();
689        let cell = adapter.get_cell(0, 100).unwrap();
690        assert!(
691            cell.is_none(),
692            "FALSIFIED: Out of bounds column should return None"
693        );
694    }
695
696    #[test]
697    fn f008_adapter_empty() {
698        let adapter = DatasetAdapter::empty();
699        assert_eq!(adapter.row_count(), 0);
700        assert_eq!(adapter.column_count(), 0);
701        assert!(adapter.is_empty());
702    }
703
704    #[test]
705    fn f009_adapter_empty_get_cell() {
706        let adapter = DatasetAdapter::empty();
707        let cell = adapter.get_cell(0, 0).unwrap();
708        assert!(
709            cell.is_none(),
710            "FALSIFIED: Empty adapter should return None"
711        );
712    }
713
714    #[test]
715    fn f010_adapter_field_name() {
716        let adapter = create_test_adapter();
717        assert_eq!(adapter.field_name(0), Some("id"));
718        assert_eq!(adapter.field_name(1), Some("value"));
719        assert_eq!(adapter.field_name(100), None);
720    }
721
722    #[test]
723    fn f011_adapter_field_type() {
724        let adapter = create_test_adapter();
725        let type_str = adapter.field_type(0).unwrap();
726        assert!(type_str.contains("Utf8"), "FALSIFIED: id should be Utf8");
727    }
728
729    #[test]
730    fn f012_adapter_field_nullable() {
731        let adapter = create_test_adapter();
732        assert_eq!(
733            adapter.field_nullable(0),
734            Some(false),
735            "FALSIFIED: id should not be nullable"
736        );
737    }
738
739    #[test]
740    fn f013_adapter_column_widths() {
741        let adapter = create_test_adapter();
742        let widths = adapter.calculate_column_widths(80, 5);
743        assert_eq!(
744            widths.len(),
745            3,
746            "FALSIFIED: Should have width for each column"
747        );
748        for (i, w) in widths.iter().enumerate() {
749            assert!(*w >= 3, "FALSIFIED: Column {} width {} below minimum", i, w);
750        }
751    }
752
753    #[test]
754    fn f014_adapter_column_widths_constrained() {
755        let adapter = create_test_adapter();
756        let widths = adapter.calculate_column_widths(15, 5);
757        let total: u16 = widths.iter().sum();
758        let separators = (widths.len() as u16).saturating_sub(1);
759        assert!(
760            total + separators <= 15,
761            "FALSIFIED: Total width {} exceeds constraint 15",
762            total + separators
763        );
764    }
765
766    #[test]
767    fn f015_adapter_locate_row_first_batch() {
768        let adapter = create_test_adapter();
769        let loc = adapter.locate_row(0);
770        assert_eq!(loc, Some((0, 0)), "FALSIFIED: Row 0 should be in batch 0");
771    }
772
773    #[test]
774    fn f016_adapter_locate_row_second_batch() {
775        let adapter = create_test_adapter();
776        let loc = adapter.locate_row(5);
777        assert_eq!(
778            loc,
779            Some((1, 0)),
780            "FALSIFIED: Row 5 should be first row of batch 1"
781        );
782    }
783
784    #[test]
785    fn f017_adapter_locate_row_last() {
786        let adapter = create_test_adapter();
787        let loc = adapter.locate_row(9);
788        assert_eq!(
789            loc,
790            Some((1, 4)),
791            "FALSIFIED: Row 9 should be last row of batch 1"
792        );
793    }
794
795    #[test]
796    fn f018_adapter_locate_row_out_of_bounds() {
797        let adapter = create_test_adapter();
798        let loc = adapter.locate_row(100);
799        assert_eq!(loc, None, "FALSIFIED: Out of bounds should return None");
800    }
801
802    #[test]
803    fn f019_adapter_is_clone() {
804        let adapter = create_test_adapter();
805        let cloned = adapter.clone();
806        assert_eq!(adapter.row_count(), cloned.row_count());
807        assert_eq!(adapter.column_count(), cloned.column_count());
808    }
809
810    #[test]
811    fn f020_adapter_schema_o1() {
812        let adapter = create_test_adapter();
813        for _ in 0..10000 {
814            let _ = adapter.schema();
815        }
816    }
817
818    #[test]
819    fn f021_adapter_row_count_o1() {
820        let adapter = create_test_adapter();
821        for _ in 0..10000 {
822            let _ = adapter.row_count();
823        }
824    }
825
826    #[test]
827    fn f022_adapter_int_formatting() {
828        let adapter = create_test_adapter();
829        let cell = adapter.get_cell(0, 1).unwrap().unwrap();
830        assert_eq!(cell, "0", "FALSIFIED: First value should be 0");
831    }
832
833    #[test]
834    fn f023_adapter_float_formatting() {
835        let adapter = create_test_adapter();
836        let cell = adapter.get_cell(1, 2).unwrap().unwrap();
837        assert!(cell.contains("0.1"), "FALSIFIED: Score should be ~0.1");
838    }
839
840    #[test]
841    fn f024_adapter_large_row_index() {
842        let adapter = create_test_adapter();
843        let cell = adapter.get_cell(usize::MAX, 0).unwrap();
844        assert!(cell.is_none(), "FALSIFIED: usize::MAX should not panic");
845    }
846
847    #[test]
848    fn f025_adapter_large_col_index() {
849        let adapter = create_test_adapter();
850        let cell = adapter.get_cell(0, usize::MAX).unwrap();
851        assert!(
852            cell.is_none(),
853            "FALSIFIED: usize::MAX column should not panic"
854        );
855    }
856
857    #[test]
858    fn f026_adapter_from_dataset() {
859        let schema = create_test_schema();
860        let batch = create_test_batch(&schema, 0, 5);
861        let dataset = ArrowDataset::from_batch(batch).unwrap();
862
863        let adapter = DatasetAdapter::from_dataset(&dataset).unwrap();
864        assert_eq!(adapter.row_count(), 5);
865        assert_eq!(adapter.column_count(), 3);
866        assert_eq!(adapter.field_name(0), Some("id"));
867    }
868
869    #[test]
870    fn f027_adapter_single_batch() {
871        let schema = create_test_schema();
872        let batch = create_test_batch(&schema, 0, 10);
873        let adapter = DatasetAdapter::from_batches(vec![batch], schema).unwrap();
874
875        assert_eq!(adapter.row_count(), 10);
876        assert_eq!(adapter.get_cell(0, 0).unwrap(), Some("id_0".to_string()));
877        assert_eq!(adapter.get_cell(9, 0).unwrap(), Some("id_9".to_string()));
878    }
879
880    #[test]
881    fn f028_adapter_multi_batch_boundaries() {
882        let schema = create_test_schema();
883        let batch1 = create_test_batch(&schema, 0, 3);
884        let batch2 = create_test_batch(&schema, 3, 3);
885        let batch3 = create_test_batch(&schema, 6, 3);
886        let adapter =
887            DatasetAdapter::from_batches(vec![batch1, batch2, batch3], schema.clone()).unwrap();
888
889        assert_eq!(adapter.row_count(), 9);
890        assert_eq!(adapter.get_cell(2, 0).unwrap(), Some("id_2".to_string()));
891        assert_eq!(adapter.get_cell(3, 0).unwrap(), Some("id_3".to_string()));
892        assert_eq!(adapter.get_cell(8, 0).unwrap(), Some("id_8".to_string()));
893    }
894
895    #[test]
896    fn f029_adapter_empty_schema_columns() {
897        let schema = create_test_schema();
898        let batch1 = create_test_batch(&schema, 0, 5);
899        let batch2 = create_test_batch(&schema, 5, 5);
900        let adapter = DatasetAdapter::from_batches(vec![batch1, batch2], schema.clone()).unwrap();
901
902        let widths = adapter.calculate_column_widths(100, 10);
903        assert_eq!(widths.len(), 3);
904    }
905
906    #[test]
907    fn f030_adapter_empty_batches() {
908        let schema = create_test_schema();
909        let adapter = DatasetAdapter::from_batches(vec![], schema).unwrap();
910        assert!(adapter.is_empty());
911        assert_eq!(adapter.row_count(), 0);
912        assert_eq!(adapter.get_cell(0, 0).unwrap(), None);
913    }
914
915    #[test]
916    fn f031_adapter_empty_schema_field_names() {
917        let schema = create_test_schema();
918        let adapter = DatasetAdapter::from_batches(vec![], schema).unwrap();
919        let names = adapter.field_names();
920        assert_eq!(names, vec!["id", "value", "score"]);
921    }
922
923    // === NEW TESTS FOR STREAMING AND SEARCH ===
924
925    #[test]
926    fn f032_adapter_is_streaming() {
927        let adapter = create_test_adapter();
928        assert!(
929            !adapter.is_streaming(),
930            "FALSIFIED: Small dataset should be InMemory"
931        );
932    }
933
934    #[test]
935    fn f033_adapter_streaming_mode() {
936        let schema = create_test_schema();
937        let batch = create_test_batch(&schema, 0, 5);
938        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
939        assert!(
940            adapter.is_streaming(),
941            "FALSIFIED: Should be Streaming mode"
942        );
943        assert_eq!(adapter.row_count(), 5);
944    }
945
946    #[test]
947    fn f034_adapter_search_finds_match() {
948        let adapter = create_test_adapter();
949        let result = adapter.search("id_5");
950        assert_eq!(
951            result,
952            Some(5),
953            "FALSIFIED: Search should find 'id_5' at row 5"
954        );
955    }
956
957    #[test]
958    fn f035_adapter_search_no_match() {
959        let adapter = create_test_adapter();
960        let result = adapter.search("nonexistent_value");
961        assert_eq!(result, None, "FALSIFIED: Search should return None");
962    }
963
964    #[test]
965    fn f036_adapter_search_empty_query() {
966        let adapter = create_test_adapter();
967        let result = adapter.search("");
968        assert_eq!(result, None, "FALSIFIED: Empty query should return None");
969    }
970
971    #[test]
972    fn f037_adapter_search_case_insensitive() {
973        let adapter = create_test_adapter();
974        let result = adapter.search("ID_3");
975        assert_eq!(
976            result,
977            Some(3),
978            "FALSIFIED: Search should be case insensitive"
979        );
980    }
981
982    #[test]
983    fn f038_adapter_search_from_wraps() {
984        let adapter = create_test_adapter();
985        // Search from row 8, should wrap and find id_0
986        let result = adapter.search_from("id_0", 8);
987        assert_eq!(result, Some(0), "FALSIFIED: Search should wrap around");
988    }
989
990    #[test]
991    fn f039_adapter_unicode_width() {
992        // Test that unicode width is correctly calculated
993        let schema = Arc::new(Schema::new(vec![Field::new(
994            "emoji",
995            DataType::Utf8,
996            false,
997        )]));
998
999        let batch = RecordBatch::try_new(
1000            schema.clone(),
1001            vec![Arc::new(StringArray::from(vec!["👨‍👩‍👧‍👦", "hello"]))],
1002        )
1003        .unwrap();
1004
1005        let adapter = DatasetAdapter::from_batches(vec![batch], schema).unwrap();
1006        let widths = adapter.calculate_column_widths(80, 10);
1007
1008        // Emoji should have visual width of 2 per component,
1009        // family emoji is complex but should be handled
1010        assert!(
1011            widths[0] >= 3,
1012            "FALSIFIED: Column width should be at least minimum"
1013        );
1014    }
1015
1016    // === Additional coverage tests for StreamingAdapter ===
1017
1018    #[test]
1019    fn f040_streaming_adapter_get_cell() {
1020        let schema = create_test_schema();
1021        let batch = create_test_batch(&schema, 0, 5);
1022        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1023
1024        let cell = adapter.get_cell(0, 0).unwrap();
1025        assert_eq!(cell, Some("id_0".to_string()));
1026    }
1027
1028    #[test]
1029    fn f041_streaming_adapter_field_name() {
1030        let schema = create_test_schema();
1031        let batch = create_test_batch(&schema, 0, 5);
1032        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1033
1034        assert_eq!(adapter.field_name(0), Some("id"));
1035        assert_eq!(adapter.field_name(1), Some("value"));
1036        assert_eq!(adapter.field_name(100), None);
1037    }
1038
1039    #[test]
1040    fn f042_streaming_adapter_field_type() {
1041        let schema = create_test_schema();
1042        let batch = create_test_batch(&schema, 0, 5);
1043        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1044
1045        let type_str = adapter.field_type(0).unwrap();
1046        assert!(type_str.contains("Utf8"));
1047        assert!(adapter.field_type(100).is_none());
1048    }
1049
1050    #[test]
1051    fn f043_streaming_adapter_field_nullable() {
1052        let schema = create_test_schema();
1053        let batch = create_test_batch(&schema, 0, 5);
1054        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1055
1056        assert_eq!(adapter.field_nullable(0), Some(false));
1057        assert!(adapter.field_nullable(100).is_none());
1058    }
1059
1060    #[test]
1061    fn f044_streaming_adapter_locate_row() {
1062        let schema = create_test_schema();
1063        let batch1 = create_test_batch(&schema, 0, 5);
1064        let batch2 = create_test_batch(&schema, 5, 5);
1065        let adapter = DatasetAdapter::streaming_from_batches(vec![batch1, batch2], schema).unwrap();
1066
1067        assert_eq!(adapter.locate_row(0), Some((0, 0)));
1068        assert_eq!(adapter.locate_row(4), Some((0, 4)));
1069        assert_eq!(adapter.locate_row(5), Some((1, 0)));
1070        assert_eq!(adapter.locate_row(9), Some((1, 4)));
1071        assert_eq!(adapter.locate_row(100), None);
1072    }
1073
1074    #[test]
1075    fn f045_streaming_adapter_column_widths() {
1076        let schema = create_test_schema();
1077        let batch = create_test_batch(&schema, 0, 5);
1078        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1079
1080        let widths = adapter.calculate_column_widths(80, 5);
1081        assert_eq!(widths.len(), 3);
1082        for w in &widths {
1083            assert!(*w >= 3);
1084        }
1085    }
1086
1087    #[test]
1088    fn f046_streaming_adapter_field_names() {
1089        let schema = create_test_schema();
1090        let batch = create_test_batch(&schema, 0, 5);
1091        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1092
1093        let names = adapter.field_names();
1094        assert_eq!(names, vec!["id", "value", "score"]);
1095    }
1096
1097    #[test]
1098    fn f047_streaming_adapter_out_of_bounds() {
1099        let schema = create_test_schema();
1100        let batch = create_test_batch(&schema, 0, 5);
1101        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1102
1103        // Row out of bounds
1104        assert_eq!(adapter.get_cell(100, 0).unwrap(), None);
1105        // Column out of bounds
1106        assert_eq!(adapter.get_cell(0, 100).unwrap(), None);
1107    }
1108
1109    #[test]
1110    fn f048_streaming_adapter_schema() {
1111        let schema = create_test_schema();
1112        let batch = create_test_batch(&schema, 0, 5);
1113        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1114
1115        assert_eq!(adapter.schema().fields().len(), 3);
1116    }
1117
1118    #[test]
1119    fn f049_search_from_empty_query() {
1120        let adapter = create_test_adapter();
1121        let result = adapter.search_from("", 0);
1122        assert_eq!(result, None);
1123    }
1124
1125    #[test]
1126    fn f050_search_from_no_wrap_needed() {
1127        let adapter = create_test_adapter();
1128        // Search from row 0, should find id_5 at row 5
1129        let result = adapter.search_from("id_5", 0);
1130        assert_eq!(result, Some(5));
1131    }
1132
1133    #[test]
1134    fn f051_search_from_no_match() {
1135        let adapter = create_test_adapter();
1136        let result = adapter.search_from("nonexistent", 0);
1137        assert_eq!(result, None);
1138    }
1139
1140    #[test]
1141    fn f052_streaming_adapter_empty_batches() {
1142        let schema = create_test_schema();
1143        let adapter = DatasetAdapter::streaming_from_batches(vec![], schema).unwrap();
1144
1145        assert_eq!(adapter.row_count(), 0);
1146        assert_eq!(adapter.column_count(), 3);
1147        assert!(adapter.is_streaming());
1148    }
1149
1150    #[test]
1151    fn f053_streaming_adapter_column_widths_empty() {
1152        let schema = Arc::new(Schema::empty());
1153        let adapter = DatasetAdapter::streaming_from_batches(vec![], schema).unwrap();
1154
1155        let widths = adapter.calculate_column_widths(80, 10);
1156        assert!(widths.is_empty());
1157    }
1158
1159    #[test]
1160    fn f054_streaming_adapter_column_widths_constrained() {
1161        let schema = create_test_schema();
1162        let batch = create_test_batch(&schema, 0, 5);
1163        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1164
1165        // Very constrained width
1166        let widths = adapter.calculate_column_widths(15, 5);
1167        let total: u16 = widths.iter().sum();
1168        let separators = (widths.len() as u16).saturating_sub(1);
1169        assert!(total + separators <= 15);
1170    }
1171
1172    #[test]
1173    fn f055_in_memory_adapter_empty_row_count() {
1174        let schema = create_test_schema();
1175        let adapter = DatasetAdapter::in_memory_from_batches(vec![], schema.clone()).unwrap();
1176
1177        assert_eq!(adapter.row_count(), 0);
1178        assert!(!adapter.is_streaming());
1179    }
1180
1181    #[test]
1182    fn f056_in_memory_adapter_locate_row_boundary() {
1183        let schema = create_test_schema();
1184        let batch1 = create_test_batch(&schema, 0, 3);
1185        let batch2 = create_test_batch(&schema, 3, 3);
1186        let batch3 = create_test_batch(&schema, 6, 4);
1187        let adapter =
1188            DatasetAdapter::in_memory_from_batches(vec![batch1, batch2, batch3], schema).unwrap();
1189
1190        // Test exact batch boundaries
1191        assert_eq!(adapter.locate_row(2), Some((0, 2))); // Last of batch 0
1192        assert_eq!(adapter.locate_row(3), Some((1, 0))); // First of batch 1
1193        assert_eq!(adapter.locate_row(5), Some((1, 2))); // Last of batch 1
1194        assert_eq!(adapter.locate_row(6), Some((2, 0))); // First of batch 2
1195        assert_eq!(adapter.locate_row(9), Some((2, 3))); // Last of batch 2
1196    }
1197
1198    #[test]
1199    fn f057_search_on_streaming_adapter() {
1200        let schema = create_test_schema();
1201        let batch = create_test_batch(&schema, 0, 10);
1202        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1203
1204        let result = adapter.search("id_7");
1205        assert_eq!(result, Some(7));
1206    }
1207
1208    #[test]
1209    fn f058_search_from_on_streaming_adapter() {
1210        let schema = create_test_schema();
1211        let batch = create_test_batch(&schema, 0, 10);
1212        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1213
1214        // Search from row 8, wraps to find id_3
1215        let result = adapter.search_from("id_3", 8);
1216        assert_eq!(result, Some(3));
1217    }
1218
1219    #[test]
1220    fn f059_search_partial_match() {
1221        let adapter = create_test_adapter();
1222        // Should match "id_" prefix
1223        let result = adapter.search("id_");
1224        assert_eq!(result, Some(0));
1225    }
1226
1227    #[test]
1228    fn f060_search_numeric_value() {
1229        let adapter = create_test_adapter();
1230        // Search for numeric value in the value column
1231        let result = adapter.search("10");
1232        assert!(result.is_some());
1233    }
1234
1235    #[test]
1236    fn f061_empty_adapter_search() {
1237        let adapter = DatasetAdapter::empty();
1238        assert_eq!(adapter.search("anything"), None);
1239        assert_eq!(adapter.search_from("anything", 0), None);
1240    }
1241
1242    #[test]
1243    fn f062_column_widths_zero_sample() {
1244        let adapter = create_test_adapter();
1245        let widths = adapter.calculate_column_widths(80, 0);
1246        // Should still have widths based on headers
1247        assert_eq!(widths.len(), 3);
1248    }
1249
1250    #[test]
1251    fn f063_column_widths_large_sample() {
1252        let adapter = create_test_adapter();
1253        // Sample more rows than exist
1254        let widths = adapter.calculate_column_widths(80, 1000);
1255        assert_eq!(widths.len(), 3);
1256    }
1257
1258    #[test]
1259    fn f064_streaming_locate_row_exact_boundary() {
1260        let schema = create_test_schema();
1261        let batch1 = create_test_batch(&schema, 0, 5);
1262        let batch2 = create_test_batch(&schema, 5, 5);
1263        let adapter = DatasetAdapter::streaming_from_batches(vec![batch1, batch2], schema).unwrap();
1264
1265        // Test the binary_search Ok branch
1266        let loc = adapter.locate_row(0);
1267        assert_eq!(loc, Some((0, 0)));
1268
1269        let loc = adapter.locate_row(5);
1270        assert_eq!(loc, Some((1, 0)));
1271    }
1272
1273    #[test]
1274    fn f065_in_memory_adapter_debug() {
1275        let adapter = create_test_adapter();
1276        let debug = format!("{:?}", adapter);
1277        assert!(debug.contains("InMemory"));
1278    }
1279
1280    #[test]
1281    fn f066_streaming_adapter_debug() {
1282        let schema = create_test_schema();
1283        let batch = create_test_batch(&schema, 0, 5);
1284        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1285        let debug = format!("{:?}", adapter);
1286        assert!(debug.contains("Streaming"));
1287    }
1288
1289    #[test]
1290    fn f067_in_memory_empty_direct() {
1291        let adapter = InMemoryAdapter::empty();
1292        assert_eq!(adapter.row_count(), 0);
1293        assert_eq!(adapter.column_count(), 0);
1294        assert!(adapter.schema().fields().is_empty());
1295    }
1296
1297    #[test]
1298    fn f068_adapter_from_dataset_small() {
1299        let schema = create_test_schema();
1300        let batch = create_test_batch(&schema, 0, 50);
1301        let dataset = ArrowDataset::from_batch(batch).unwrap();
1302
1303        let adapter = DatasetAdapter::from_dataset(&dataset).unwrap();
1304        // Should be in-memory mode for small datasets
1305        assert!(!adapter.is_streaming());
1306        assert_eq!(adapter.row_count(), 50);
1307    }
1308
1309    // ========================================================================
1310    // Additional TUI Adapter Tests for Coverage
1311    // ========================================================================
1312
1313    #[test]
1314    fn f069_in_memory_adapter_get_cell_batch_not_found() {
1315        // Test when batch index is invalid
1316        let schema = create_test_schema();
1317        let adapter = InMemoryAdapter::new(vec![], schema).unwrap();
1318        let result = adapter.get_cell(0, 0).unwrap();
1319        assert!(result.is_none());
1320    }
1321
1322    #[test]
1323    fn f070_streaming_adapter_get_cell_batch_not_found() {
1324        // Test when batch index is invalid in streaming mode
1325        let schema = create_test_schema();
1326        let adapter = StreamingAdapter::new(vec![], schema).unwrap();
1327        let result = adapter.get_cell(0, 0).unwrap();
1328        assert!(result.is_none());
1329    }
1330
1331    #[test]
1332    fn f071_in_memory_adapter_locate_row_at_batch_boundary() {
1333        let schema = create_test_schema();
1334        let batch1 = create_test_batch(&schema, 0, 5);
1335        let batch2 = create_test_batch(&schema, 5, 5);
1336        let adapter = InMemoryAdapter::new(vec![batch1, batch2], schema).unwrap();
1337
1338        // Test exact batch boundary (binary_search returns Ok)
1339        let loc = adapter.locate_row(5);
1340        assert_eq!(loc, Some((1, 0)));
1341
1342        // Test one before boundary
1343        let loc = adapter.locate_row(4);
1344        assert_eq!(loc, Some((0, 4)));
1345    }
1346
1347    #[test]
1348    fn f072_streaming_adapter_locate_row_at_batch_boundary() {
1349        let schema = create_test_schema();
1350        let batch1 = create_test_batch(&schema, 0, 5);
1351        let batch2 = create_test_batch(&schema, 5, 5);
1352        let adapter = StreamingAdapter::new(vec![batch1, batch2], schema).unwrap();
1353
1354        // Test exact batch boundary
1355        let loc = adapter.locate_row(5);
1356        assert_eq!(loc, Some((1, 0)));
1357    }
1358
1359    #[test]
1360    fn f073_in_memory_adapter_schema_access() {
1361        let schema = create_test_schema();
1362        let adapter = InMemoryAdapter::new(vec![], schema.clone()).unwrap();
1363        assert_eq!(adapter.schema().fields().len(), 3);
1364    }
1365
1366    #[test]
1367    fn f074_streaming_adapter_schema_access() {
1368        let schema = create_test_schema();
1369        let adapter = StreamingAdapter::new(vec![], schema.clone()).unwrap();
1370        assert_eq!(adapter.schema().fields().len(), 3);
1371    }
1372
1373    #[test]
1374    fn f075_in_memory_adapter_row_count() {
1375        let schema = create_test_schema();
1376        let batch = create_test_batch(&schema, 0, 7);
1377        let adapter = InMemoryAdapter::new(vec![batch], schema).unwrap();
1378        assert_eq!(adapter.row_count(), 7);
1379    }
1380
1381    #[test]
1382    fn f076_streaming_adapter_row_count() {
1383        let schema = create_test_schema();
1384        let batch = create_test_batch(&schema, 0, 7);
1385        let adapter = StreamingAdapter::new(vec![batch], schema).unwrap();
1386        assert_eq!(adapter.row_count(), 7);
1387    }
1388
1389    #[test]
1390    fn f077_in_memory_adapter_column_count() {
1391        let schema = create_test_schema();
1392        let adapter = InMemoryAdapter::new(vec![], schema).unwrap();
1393        assert_eq!(adapter.column_count(), 3);
1394    }
1395
1396    #[test]
1397    fn f078_streaming_adapter_column_count() {
1398        let schema = create_test_schema();
1399        let adapter = StreamingAdapter::new(vec![], schema).unwrap();
1400        assert_eq!(adapter.column_count(), 3);
1401    }
1402
1403    #[test]
1404    fn f079_in_memory_adapter_field_names() {
1405        let schema = create_test_schema();
1406        let adapter = InMemoryAdapter::new(vec![], schema).unwrap();
1407        let names = adapter.field_names();
1408        assert_eq!(names, vec!["id", "value", "score"]);
1409    }
1410
1411    #[test]
1412    fn f080_in_memory_adapter_field_name_out_of_bounds() {
1413        let schema = create_test_schema();
1414        let adapter = InMemoryAdapter::new(vec![], schema).unwrap();
1415        assert!(adapter.field_name(100).is_none());
1416    }
1417
1418    #[test]
1419    fn f081_in_memory_adapter_field_type_out_of_bounds() {
1420        let schema = create_test_schema();
1421        let adapter = InMemoryAdapter::new(vec![], schema).unwrap();
1422        assert!(adapter.field_type(100).is_none());
1423    }
1424
1425    #[test]
1426    fn f082_in_memory_adapter_field_nullable_out_of_bounds() {
1427        let schema = create_test_schema();
1428        let adapter = InMemoryAdapter::new(vec![], schema).unwrap();
1429        assert!(adapter.field_nullable(100).is_none());
1430    }
1431
1432    #[test]
1433    fn f083_streaming_adapter_field_name_out_of_bounds() {
1434        let schema = create_test_schema();
1435        let adapter = StreamingAdapter::new(vec![], schema).unwrap();
1436        assert!(adapter.field_name(100).is_none());
1437    }
1438
1439    #[test]
1440    fn f084_streaming_adapter_field_type_out_of_bounds() {
1441        let schema = create_test_schema();
1442        let adapter = StreamingAdapter::new(vec![], schema).unwrap();
1443        assert!(adapter.field_type(100).is_none());
1444    }
1445
1446    #[test]
1447    fn f085_streaming_adapter_field_nullable_out_of_bounds() {
1448        let schema = create_test_schema();
1449        let adapter = StreamingAdapter::new(vec![], schema).unwrap();
1450        assert!(adapter.field_nullable(100).is_none());
1451    }
1452
1453    #[test]
1454    fn f086_in_memory_calculate_column_widths_empty_schema() {
1455        let schema = Arc::new(Schema::empty());
1456        let adapter = InMemoryAdapter::new(vec![], schema).unwrap();
1457        let widths = adapter.calculate_column_widths(80, 10);
1458        assert!(widths.is_empty());
1459    }
1460
1461    #[test]
1462    fn f087_in_memory_calculate_column_widths_scaling() {
1463        let schema = create_test_schema();
1464        let batch = create_test_batch(&schema, 0, 5);
1465        let adapter = InMemoryAdapter::new(vec![batch], schema).unwrap();
1466
1467        // Test with very narrow width to force scaling
1468        let widths = adapter.calculate_column_widths(12, 5);
1469        let total: u16 = widths.iter().sum();
1470        let separators = (widths.len() as u16).saturating_sub(1);
1471        assert!(total + separators <= 12);
1472    }
1473
1474    #[test]
1475    fn f088_streaming_calculate_column_widths_scaling() {
1476        let schema = create_test_schema();
1477        let batch = create_test_batch(&schema, 0, 5);
1478        let adapter = StreamingAdapter::new(vec![batch], schema).unwrap();
1479
1480        // Test with very narrow width to force scaling
1481        let widths = adapter.calculate_column_widths(12, 5);
1482        let total: u16 = widths.iter().sum();
1483        let separators = (widths.len() as u16).saturating_sub(1);
1484        assert!(total + separators <= 12);
1485    }
1486
1487    #[test]
1488    fn f089_adapter_search_empty_dataset() {
1489        let adapter = DatasetAdapter::empty();
1490        assert!(adapter.search("anything").is_none());
1491    }
1492
1493    #[test]
1494    fn f090_adapter_search_from_start_row_beyond_total() {
1495        let adapter = create_test_adapter();
1496        // Start from row 100 (beyond total of 10)
1497        let result = adapter.search_from("id_0", 100);
1498        // Should wrap and find id_0
1499        assert_eq!(result, Some(0));
1500    }
1501
1502    #[test]
1503    fn f091_in_memory_locate_row_binary_search_ok_branch() {
1504        // Create batches such that row 0 is exactly at batch boundary offset
1505        let schema = create_test_schema();
1506        let batch1 = create_test_batch(&schema, 0, 3);
1507        let batch2 = create_test_batch(&schema, 3, 3);
1508        let adapter = InMemoryAdapter::new(vec![batch1, batch2], schema).unwrap();
1509
1510        // Row 3 should be at offset 3, which is in batch 1
1511        let loc = adapter.locate_row(3);
1512        assert_eq!(loc, Some((1, 0)));
1513
1514        // Row 0 is at offset 0, binary_search returns Ok(0)
1515        let loc = adapter.locate_row(0);
1516        assert_eq!(loc, Some((0, 0)));
1517    }
1518
1519    #[test]
1520    fn f092_streaming_locate_row_binary_search_ok_branch() {
1521        let schema = create_test_schema();
1522        let batch1 = create_test_batch(&schema, 0, 3);
1523        let batch2 = create_test_batch(&schema, 3, 3);
1524        let adapter = StreamingAdapter::new(vec![batch1, batch2], schema).unwrap();
1525
1526        let loc = adapter.locate_row(3);
1527        assert_eq!(loc, Some((1, 0)));
1528
1529        let loc = adapter.locate_row(0);
1530        assert_eq!(loc, Some((0, 0)));
1531    }
1532
1533    #[test]
1534    fn f093_adapter_field_type_streaming_mode() {
1535        let schema = create_test_schema();
1536        let batch = create_test_batch(&schema, 0, 5);
1537        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1538
1539        let type_str = adapter.field_type(0);
1540        assert!(type_str.is_some());
1541        assert!(type_str.unwrap().contains("Utf8"));
1542    }
1543
1544    #[test]
1545    fn f094_adapter_field_nullable_streaming_mode() {
1546        let schema = create_test_schema();
1547        let batch = create_test_batch(&schema, 0, 5);
1548        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1549
1550        let nullable = adapter.field_nullable(0);
1551        assert_eq!(nullable, Some(false));
1552    }
1553
1554    #[test]
1555    fn f095_adapter_locate_row_streaming_mode() {
1556        let schema = create_test_schema();
1557        let batch = create_test_batch(&schema, 0, 5);
1558        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1559
1560        let loc = adapter.locate_row(2);
1561        assert_eq!(loc, Some((0, 2)));
1562
1563        let loc_oob = adapter.locate_row(100);
1564        assert!(loc_oob.is_none());
1565    }
1566
1567    #[test]
1568    fn f096_in_memory_adapter_with_many_batches() {
1569        let schema = create_test_schema();
1570        let batches: Vec<_> = (0..10)
1571            .map(|i| create_test_batch(&schema, i * 5, 5))
1572            .collect();
1573        let adapter = InMemoryAdapter::new(batches, schema).unwrap();
1574
1575        assert_eq!(adapter.row_count(), 50);
1576
1577        // Test locating rows in different batches
1578        assert_eq!(adapter.locate_row(0), Some((0, 0)));
1579        assert_eq!(adapter.locate_row(7), Some((1, 2)));
1580        assert_eq!(adapter.locate_row(49), Some((9, 4)));
1581    }
1582
1583    #[test]
1584    fn f097_streaming_adapter_with_many_batches() {
1585        let schema = create_test_schema();
1586        let batches: Vec<_> = (0..10)
1587            .map(|i| create_test_batch(&schema, i * 5, 5))
1588            .collect();
1589        let adapter = StreamingAdapter::new(batches, schema).unwrap();
1590
1591        assert_eq!(adapter.row_count(), 50);
1592        assert_eq!(adapter.locate_row(7), Some((1, 2)));
1593    }
1594
1595    #[test]
1596    fn f098_adapter_search_in_numeric_column() {
1597        let adapter = create_test_adapter();
1598        // Values column contains "0", "10", "20", etc.
1599        let result = adapter.search("30");
1600        assert!(result.is_some());
1601    }
1602
1603    #[test]
1604    fn f099_adapter_search_partial_match() {
1605        let adapter = create_test_adapter();
1606        // Should find partial matches
1607        let result = adapter.search("d_3");
1608        assert_eq!(result, Some(3));
1609    }
1610
1611    #[test]
1612    fn f100_adapter_is_empty_with_batches() {
1613        let schema = create_test_schema();
1614        let batch = create_test_batch(&schema, 0, 5);
1615        let adapter = DatasetAdapter::from_batches(vec![batch], schema).unwrap();
1616        assert!(!adapter.is_empty());
1617    }
1618
1619    #[test]
1620    fn f101_streaming_adapter_search() {
1621        let schema = create_test_schema();
1622        let batch = create_test_batch(&schema, 0, 10);
1623        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1624
1625        let result = adapter.search("id_5");
1626        assert_eq!(result, Some(5));
1627    }
1628
1629    #[test]
1630    fn f102_streaming_adapter_search_from() {
1631        let schema = create_test_schema();
1632        let batch = create_test_batch(&schema, 0, 10);
1633        let adapter = DatasetAdapter::streaming_from_batches(vec![batch], schema).unwrap();
1634
1635        // Search from row 7, should wrap and find id_2
1636        let result = adapter.search_from("id_2", 7);
1637        assert_eq!(result, Some(2));
1638    }
1639
1640    #[test]
1641    fn f103_calculate_column_widths_with_unicode() {
1642        // Test that unicode width calculation works correctly
1643        let schema = Arc::new(Schema::new(vec![Field::new("name", DataType::Utf8, false)]));
1644
1645        let batch = RecordBatch::try_new(
1646            schema.clone(),
1647            vec![Arc::new(StringArray::from(vec!["Hello", "World"]))],
1648        )
1649        .unwrap();
1650
1651        let adapter = DatasetAdapter::from_batches(vec![batch], schema).unwrap();
1652        let widths = adapter.calculate_column_widths(80, 10);
1653        assert!(!widths.is_empty());
1654        assert!(widths[0] >= 4); // "name" has 4 chars
1655    }
1656
1657    #[test]
1658    fn f104_in_memory_empty_direct_methods() {
1659        let adapter = InMemoryAdapter::empty();
1660        assert_eq!(adapter.row_count(), 0);
1661        assert_eq!(adapter.column_count(), 0);
1662        assert!(adapter.field_name(0).is_none());
1663        assert!(adapter.field_type(0).is_none());
1664        assert!(adapter.field_nullable(0).is_none());
1665        assert!(adapter.locate_row(0).is_none());
1666    }
1667}