1use anyhow::Result;
2use fuzzy_matcher::skim::SkimMatcherV2;
3use fuzzy_matcher::FuzzyMatcher;
4use serde_json::{json, Value};
5use std::sync::Arc;
6use tracing::{debug, info};
7
8use crate::data::data_provider::DataProvider;
9use crate::data::datatable::{DataRow, DataTable, DataValue};
10use crate::data::datavalue_compare::compare_optional_datavalues;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum SortOrder {
15 Ascending,
16 Descending,
17 None,
18}
19
20#[derive(Debug, Clone)]
22pub struct SortState {
23 pub column: Option<usize>,
25 pub order: SortOrder,
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum VirtualColumnPosition {
32 Left,
34 Right,
36 Index(usize),
38}
39
40#[derive(Clone)]
42pub struct VirtualColumn {
43 pub name: String,
45 pub generator: Arc<dyn Fn(usize) -> String + Send + Sync>,
47 pub width: Option<usize>,
49 pub position: VirtualColumnPosition,
51}
52
53impl std::fmt::Debug for VirtualColumn {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_struct("VirtualColumn")
56 .field("name", &self.name)
57 .field("width", &self.width)
58 .field("position", &self.position)
59 .finish()
60 }
61}
62
63impl Default for SortState {
64 fn default() -> Self {
65 Self {
66 column: None,
67 order: SortOrder::None,
68 }
69 }
70}
71
72#[derive(Clone)]
75pub struct DataView {
76 source: Arc<DataTable>,
78
79 visible_rows: Vec<usize>,
81
82 visible_columns: Vec<usize>,
84
85 limit: Option<usize>,
87 offset: usize,
88
89 base_rows: Vec<usize>,
92
93 base_columns: Vec<usize>,
96
97 filter_pattern: Option<String>,
99
100 fuzzy_filter_pattern: Option<String>,
102
103 column_search_pattern: Option<String>,
105 matching_columns: Vec<(usize, String)>,
107 current_column_match: usize,
109
110 pinned_columns: Vec<usize>,
112 max_pinned_columns: usize,
114
115 sort_state: SortState,
117
118 virtual_columns: Vec<VirtualColumn>,
120}
121
122impl DataView {
123 pub fn new(source: Arc<DataTable>) -> Self {
125 let row_count = source.row_count();
126 let col_count = source.column_count();
127 let all_rows: Vec<usize> = (0..row_count).collect();
128 let all_columns: Vec<usize> = (0..col_count).collect();
129
130 Self {
131 source,
132 visible_rows: all_rows.clone(),
133 visible_columns: all_columns.clone(),
134 limit: None,
135 offset: 0,
136 base_rows: all_rows,
137 base_columns: all_columns,
138 filter_pattern: None,
139 fuzzy_filter_pattern: None,
140 column_search_pattern: None,
141 matching_columns: Vec::new(),
142 current_column_match: 0,
143 pinned_columns: Vec::new(),
144 max_pinned_columns: 4,
145 sort_state: SortState::default(),
146 virtual_columns: Vec::new(),
147 }
148 }
149
150 pub fn with_columns(mut self, columns: Vec<usize>) -> Self {
152 self.visible_columns = columns.clone();
153 self.base_columns = columns; self
155 }
156
157 pub fn hide_column(&mut self, display_index: usize) -> bool {
159 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
161 if self.pinned_columns.contains(&source_column_index) {
163 return false;
164 }
165
166 self.visible_columns.remove(display_index);
168 true
169 } else {
170 false
171 }
172 }
173
174 pub fn hide_column_by_name(&mut self, column_name: &str) -> bool {
176 if let Some(source_idx) = self.source.get_column_index(column_name) {
177 if let Some(display_idx) = self
179 .visible_columns
180 .iter()
181 .position(|&idx| idx == source_idx)
182 {
183 self.hide_column(display_idx)
184 } else {
185 false }
187 } else {
188 false
189 }
190 }
191
192 pub fn detect_empty_columns(&self) -> Vec<usize> {
194 let mut empty_columns = Vec::new();
195
196 let column_names = self.source.column_names();
198
199 for &col_idx in &self.visible_columns {
201 let column_name = column_names
202 .get(col_idx)
203 .map(|s| s.as_str())
204 .unwrap_or("unknown");
205 let mut is_empty = true;
206 let mut sample_values = Vec::new();
207
208 let rows_to_check = self.visible_rows.len().min(1000);
211
212 for &row_idx in self.visible_rows.iter().take(rows_to_check) {
213 if let Some(value) = self.source.get_value(row_idx, col_idx) {
214 if sample_values.len() < 3 {
216 sample_values.push(format!("{:?}", value));
217 }
218
219 match value {
220 DataValue::Null => continue,
221 DataValue::String(s) if s.is_empty() => continue,
222 DataValue::String(s) if s.trim().is_empty() => continue, DataValue::String(s) if s.eq_ignore_ascii_case("null") => continue, DataValue::String(s) if s == "NULL" => continue, DataValue::String(s) if s == "nil" => continue, DataValue::String(s) if s == "undefined" => continue, _ => {
228 is_empty = false;
229 break;
230 }
231 }
232 }
233 }
234
235 if is_empty {
236 tracing::debug!(
237 "Column '{}' (idx {}) detected as empty. Sample values: {:?}",
238 column_name,
239 col_idx,
240 sample_values
241 );
242 empty_columns.push(col_idx);
243 } else {
244 tracing::debug!(
245 "Column '{}' (idx {}) has non-empty values. Sample values: {:?}",
246 column_name,
247 col_idx,
248 sample_values
249 );
250 }
251 }
252
253 tracing::info!(
254 "Detected {} empty columns out of {} visible columns",
255 empty_columns.len(),
256 self.visible_columns.len()
257 );
258 empty_columns
259 }
260
261 pub fn hide_empty_columns(&mut self) -> usize {
264 let empty_columns = self.detect_empty_columns();
265 let count = empty_columns.len();
266
267 let column_names = self.source.column_names();
271 for col_idx in empty_columns {
272 if let Some(column_name) = column_names.get(col_idx) {
273 tracing::debug!(
274 "Hiding empty column '{}' (source index {})",
275 column_name,
276 col_idx
277 );
278 self.hide_column_by_name(column_name);
279 }
280 }
281
282 count
283 }
284
285 pub fn unhide_all_columns(&mut self) {
288 self.visible_columns = self.base_columns.clone();
289 }
290
291 pub fn hide_all_columns(&mut self) {
293 self.visible_columns.clear();
294 }
295
296 pub fn has_visible_columns(&self) -> bool {
298 !self.visible_columns.is_empty()
299 }
300
301 pub fn move_column_left(&mut self, display_column_index: usize) -> bool {
304 if display_column_index >= self.visible_columns.len() {
305 return false;
306 }
307
308 let pinned_count = self.pinned_columns.len();
309
310 if display_column_index < pinned_count {
312 if display_column_index == 0 {
314 if pinned_count > 1 {
316 let col = self.pinned_columns.remove(0);
317 self.pinned_columns.push(col);
318 self.rebuild_visible_columns();
319 }
320 } else {
321 self.pinned_columns
323 .swap(display_column_index - 1, display_column_index);
324 self.rebuild_visible_columns();
325 }
326 return true;
327 }
328
329 if display_column_index == pinned_count {
331 let col = self.visible_columns.remove(display_column_index);
333 self.visible_columns.push(col);
334 } else {
335 self.visible_columns
337 .swap(display_column_index - 1, display_column_index);
338 }
339 true
340 }
341
342 pub fn move_column_right(&mut self, display_column_index: usize) -> bool {
345 if display_column_index >= self.visible_columns.len() {
346 return false;
347 }
348
349 let pinned_count = self.pinned_columns.len();
350
351 if display_column_index < pinned_count {
353 if display_column_index == pinned_count - 1 {
355 if pinned_count > 1 {
357 let col = self.pinned_columns.pop().unwrap();
358 self.pinned_columns.insert(0, col);
359 self.rebuild_visible_columns();
360 }
361 } else {
362 self.pinned_columns
364 .swap(display_column_index, display_column_index + 1);
365 self.rebuild_visible_columns();
366 }
367 return true;
368 }
369
370 if display_column_index == self.visible_columns.len() - 1 {
372 let col = self.visible_columns.pop().unwrap();
374 self.visible_columns.insert(pinned_count, col);
375 } else {
376 self.visible_columns
378 .swap(display_column_index, display_column_index + 1);
379 }
380 true
381 }
382
383 pub fn move_column_left_by_name(&mut self, column_name: &str) -> bool {
385 if let Some(source_idx) = self.source.get_column_index(column_name) {
386 if let Some(visible_idx) = self
387 .visible_columns
388 .iter()
389 .position(|&idx| idx == source_idx)
390 {
391 return self.move_column_left(visible_idx);
392 }
393 }
394 false
395 }
396
397 pub fn move_column_right_by_name(&mut self, column_name: &str) -> bool {
399 if let Some(source_idx) = self.source.get_column_index(column_name) {
400 if let Some(visible_idx) = self
401 .visible_columns
402 .iter()
403 .position(|&idx| idx == source_idx)
404 {
405 return self.move_column_right(visible_idx);
406 }
407 }
408 false
409 }
410
411 pub fn get_hidden_column_names(&self) -> Vec<String> {
413 let all_columns = self.source.column_names();
414 let visible_columns = self.column_names();
415
416 all_columns
417 .into_iter()
418 .filter(|col| !visible_columns.contains(col))
419 .collect()
420 }
421
422 pub fn has_hidden_columns(&self) -> bool {
424 self.visible_columns.len() < self.source.column_count()
425 }
426
427 pub fn pin_column(&mut self, display_index: usize) -> Result<()> {
431 let source_column_index = if let Some(&idx) = self.visible_columns.get(display_index) {
433 idx
434 } else {
435 return Err(anyhow::anyhow!(
436 "Display index {} out of bounds",
437 display_index
438 ));
439 };
440
441 if self.pinned_columns.len() >= self.max_pinned_columns {
443 return Err(anyhow::anyhow!(
444 "Maximum {} pinned columns allowed",
445 self.max_pinned_columns
446 ));
447 }
448
449 if self.pinned_columns.contains(&source_column_index) {
451 return Ok(()); }
453
454 self.pinned_columns.push(source_column_index);
456
457 self.rebuild_visible_columns();
459
460 Ok(())
461 }
462
463 fn rebuild_visible_columns(&mut self) {
466 let mut new_visible_columns = Vec::new();
467
468 for &pinned_idx in &self.pinned_columns {
470 new_visible_columns.push(pinned_idx);
471 }
472
473 for col_idx in 0..self.source.column_count() {
475 if !self.pinned_columns.contains(&col_idx) {
476 new_visible_columns.push(col_idx);
477 }
478 }
479
480 self.visible_columns = new_visible_columns;
481 }
482
483 pub fn pin_column_by_name(&mut self, column_name: &str) -> Result<()> {
485 if let Some(source_idx) = self.source.get_column_index(column_name) {
486 if let Some(display_idx) = self
488 .visible_columns
489 .iter()
490 .position(|&idx| idx == source_idx)
491 {
492 self.pin_column(display_idx)
493 } else {
494 Err(anyhow::anyhow!("Column '{}' not visible", column_name))
495 }
496 } else {
497 Err(anyhow::anyhow!("Column '{}' not found", column_name))
498 }
499 }
500
501 pub fn unpin_column(&mut self, display_index: usize) -> bool {
503 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
505 if let Some(pos) = self
506 .pinned_columns
507 .iter()
508 .position(|&idx| idx == source_column_index)
509 {
510 self.pinned_columns.remove(pos);
511
512 self.rebuild_visible_columns();
514
515 true
516 } else {
517 false }
519 } else {
520 false }
522 }
523
524 pub fn unpin_column_by_name(&mut self, column_name: &str) -> bool {
526 if let Some(source_idx) = self.source.get_column_index(column_name) {
527 if let Some(display_idx) = self
529 .visible_columns
530 .iter()
531 .position(|&idx| idx == source_idx)
532 {
533 self.unpin_column(display_idx)
534 } else {
535 false }
537 } else {
538 false
539 }
540 }
541
542 pub fn clear_pinned_columns(&mut self) {
544 for col_idx in self.pinned_columns.drain(..) {
546 if !self.visible_columns.contains(&col_idx) {
547 self.visible_columns.push(col_idx);
548 }
549 }
550 }
551
552 pub fn is_column_pinned(&self, display_index: usize) -> bool {
554 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
555 self.pinned_columns.contains(&source_column_index)
556 } else {
557 false
558 }
559 }
560
561 pub fn get_pinned_columns(&self) -> &[usize] {
563 &self.pinned_columns
564 }
565
566 pub fn get_pinned_column_names(&self) -> Vec<String> {
568 let all_columns = self.source.column_names();
569 self.pinned_columns
570 .iter()
571 .filter_map(|&idx| all_columns.get(idx).cloned())
572 .collect()
573 }
574
575 pub fn get_display_columns(&self) -> Vec<usize> {
577 self.visible_columns.clone()
580 }
581
582 pub fn get_display_column_names(&self) -> Vec<String> {
584 let all_columns = self.source.column_names();
585 self.get_display_columns()
586 .iter()
587 .filter_map(|&idx| all_columns.get(idx).cloned())
588 .collect()
589 }
590
591 pub fn set_max_pinned_columns(&mut self, max: usize) {
593 self.max_pinned_columns = max;
594 while self.pinned_columns.len() > max {
596 if let Some(col_idx) = self.pinned_columns.pop() {
597 self.visible_columns.insert(0, col_idx);
598 }
599 }
600 }
601
602 pub fn with_rows(mut self, rows: Vec<usize>) -> Self {
604 self.visible_rows = rows.clone();
605 self.base_rows = rows; self
607 }
608
609 pub fn with_limit(mut self, limit: usize, offset: usize) -> Self {
611 self.limit = Some(limit);
612 self.offset = offset;
613 self
614 }
615
616 pub fn filter<F>(mut self, predicate: F) -> Self
618 where
619 F: Fn(&DataTable, usize) -> bool,
620 {
621 self.visible_rows = self
622 .visible_rows
623 .into_iter()
624 .filter(|&row_idx| predicate(&self.source, row_idx))
625 .collect();
626 self.base_rows = self.visible_rows.clone();
628 self
629 }
630
631 pub fn apply_text_filter(&mut self, pattern: &str, case_sensitive: bool) {
633 info!(
634 "DataView::apply_text_filter - pattern='{}', case_sensitive={}, thread={:?}",
635 pattern,
636 case_sensitive,
637 std::thread::current().id()
638 );
639
640 if pattern.is_empty() {
641 info!("DataView::apply_text_filter - empty pattern, clearing filter");
642 self.clear_filter();
643 return;
644 }
645
646 if self.fuzzy_filter_pattern.is_some() {
648 info!("DataView::apply_text_filter - clearing existing fuzzy filter");
649 self.fuzzy_filter_pattern = None;
650 }
651
652 self.filter_pattern = Some(pattern.to_string());
654
655 let pattern_lower = if !case_sensitive {
657 pattern.to_lowercase()
658 } else {
659 pattern.to_string()
660 };
661
662 info!(
663 "DataView::apply_text_filter - searching for '{}' in {} base rows",
664 pattern_lower,
665 self.base_rows.len()
666 );
667
668 let mut matched_count = 0;
669 let mut checked_count = 0;
670
671 self.visible_rows = self
672 .base_rows
673 .iter()
674 .copied()
675 .filter(|&row_idx| {
676 checked_count += 1;
677
678 if let Some(row) = self.source.get_row(row_idx) {
680 if checked_count <= 3 {
682 let preview = row
683 .values
684 .iter()
685 .take(5)
686 .map(|v| v.to_string())
687 .collect::<Vec<_>>()
688 .join(", ");
689 info!(
690 "DataView::apply_text_filter - row {} preview: {}",
691 row_idx, preview
692 );
693 }
694
695 for value in &row.values {
696 let text = value.to_string();
697 let text_to_match = if !case_sensitive {
698 text.to_lowercase()
699 } else {
700 text.clone()
701 };
702 if text_to_match.contains(&pattern_lower) {
703 matched_count += 1;
704 if checked_count <= 3 {
705 info!(
706 "DataView::apply_text_filter - MATCH in row {} cell: '{}'",
707 row_idx, text
708 );
709 }
710 return true;
711 }
712 }
713 }
714 false
715 })
716 .collect();
717
718 info!(
719 "DataView::apply_text_filter - checked {} rows, matched {} rows",
720 checked_count, matched_count
721 );
722 info!(
723 "DataView::apply_text_filter - final visible rows: {}",
724 self.visible_rows.len()
725 );
726
727 if let Some(sort_column) = self.sort_state.column {
729 if let Some(source_index) = self.visible_columns.get(sort_column) {
730 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
731 info!(
732 "DataView::apply_text_filter - reapplying sort on column {} (ascending={})",
733 sort_column, ascending
734 );
735 let _ = self.apply_sort_internal(*source_index, ascending);
736 }
737 }
738 }
739
740 pub fn clear_filter(&mut self) {
742 self.filter_pattern = None;
743 self.fuzzy_filter_pattern = None;
744 self.visible_rows = self.base_rows.clone();
745
746 if let Some(sort_column) = self.sort_state.column {
748 if let Some(source_index) = self.visible_columns.get(sort_column) {
749 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
750 let _ = self.apply_sort_internal(*source_index, ascending);
751 }
752 }
753 }
754
755 pub fn has_filter(&self) -> bool {
757 self.filter_pattern.is_some() || self.fuzzy_filter_pattern.is_some()
758 }
759
760 pub fn get_filter_pattern(&self) -> Option<&str> {
762 self.filter_pattern.as_deref()
763 }
764
765 pub fn get_fuzzy_filter_pattern(&self) -> Option<&str> {
767 self.fuzzy_filter_pattern.as_deref()
768 }
769
770 pub fn apply_fuzzy_filter(&mut self, pattern: &str, case_insensitive: bool) {
773 info!(
774 "DataView::apply_fuzzy_filter - pattern='{}', case_insensitive={}, thread={:?}",
775 pattern,
776 case_insensitive,
777 std::thread::current().id()
778 );
779
780 if pattern.is_empty() {
781 info!("DataView::apply_fuzzy_filter - empty pattern, clearing filter");
782 self.clear_filter();
783 return;
784 }
785
786 if self.filter_pattern.is_some() {
788 info!("DataView::apply_fuzzy_filter - clearing existing text filter");
789 self.filter_pattern = None;
790 }
791
792 self.fuzzy_filter_pattern = Some(pattern.to_string());
794
795 let use_exact = pattern.starts_with('\'');
797
798 self.visible_rows = self
799 .base_rows
800 .iter()
801 .copied()
802 .filter(|&row_idx| {
803 if let Some(row) = self.source.get_row(row_idx) {
805 let row_text = row
807 .values
808 .iter()
809 .map(|v| v.to_string())
810 .collect::<Vec<_>>()
811 .join(" ");
812
813 if use_exact && pattern.len() > 1 {
814 let exact_pattern = &pattern[1..];
816 if case_insensitive {
817 row_text
818 .to_lowercase()
819 .contains(&exact_pattern.to_lowercase())
820 } else {
821 row_text.contains(exact_pattern)
822 }
823 } else if !use_exact {
824 let matcher = if case_insensitive {
826 SkimMatcherV2::default().ignore_case()
827 } else {
828 SkimMatcherV2::default().respect_case()
829 };
830
831 matcher
833 .fuzzy_match(&row_text, pattern)
834 .map_or(false, |score| score > 0)
835 } else {
836 false
838 }
839 } else {
840 false
841 }
842 })
843 .collect();
844
845 if let Some(sort_column) = self.sort_state.column {
847 if let Some(source_index) = self.visible_columns.get(sort_column) {
848 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
849 info!(
850 "DataView::apply_fuzzy_filter - reapplying sort on column {} (ascending={})",
851 sort_column, ascending
852 );
853 let _ = self.apply_sort_internal(*source_index, ascending);
854 }
855 }
856 }
857
858 pub fn get_fuzzy_filter_indices(&self) -> Vec<usize> {
860 self.visible_rows.clone()
862 }
863
864 pub fn get_visible_rows(&self) -> Vec<usize> {
866 self.visible_rows.clone()
867 }
868
869 pub fn sort_by(mut self, column_index: usize, ascending: bool) -> Result<Self> {
872 self.apply_sort(column_index, ascending)?;
873 Ok(self)
874 }
875
876 pub fn apply_sort(&mut self, column_index: usize, ascending: bool) -> Result<()> {
879 let source_column_index = if column_index < self.visible_columns.len() {
881 self.visible_columns[column_index]
882 } else {
883 return Err(anyhow::anyhow!(
884 "Column index {} out of bounds (visible columns: {})",
885 column_index,
886 self.visible_columns.len()
887 ));
888 };
889
890 self.apply_sort_internal(source_column_index, ascending)?;
892
893 self.sort_state.column = Some(column_index);
895 self.sort_state.order = if ascending {
896 SortOrder::Ascending
897 } else {
898 SortOrder::Descending
899 };
900
901 Ok(())
902 }
903
904 fn apply_sort_internal(&mut self, source_column_index: usize, ascending: bool) -> Result<()> {
906 if source_column_index >= self.source.column_count() {
907 return Err(anyhow::anyhow!(
908 "Source column index {} out of bounds",
909 source_column_index
910 ));
911 }
912
913 let source = &self.source;
914 self.visible_rows.sort_by(|&a, &b| {
915 let val_a = source.get_value(a, source_column_index);
916 let val_b = source.get_value(b, source_column_index);
917
918 let cmp = compare_optional_datavalues(val_a, val_b);
919
920 if ascending {
921 cmp
922 } else {
923 cmp.reverse()
924 }
925 });
926
927 Ok(())
931 }
932
933 pub fn apply_multi_sort(&mut self, sort_columns: &[(usize, bool)]) -> Result<()> {
936 if sort_columns.is_empty() {
937 return Ok(());
938 }
939
940 for (col_idx, _) in sort_columns {
942 if *col_idx >= self.source.column_count() {
943 return Err(anyhow::anyhow!(
944 "Source column index {} out of bounds",
945 col_idx
946 ));
947 }
948 }
949
950 let source = &self.source;
951 self.visible_rows.sort_by(|&a, &b| {
952 for (col_idx, ascending) in sort_columns {
954 let val_a = source.get_value(a, *col_idx);
955 let val_b = source.get_value(b, *col_idx);
956
957 let cmp = compare_optional_datavalues(val_a, val_b);
958
959 if cmp != std::cmp::Ordering::Equal {
961 return if *ascending { cmp } else { cmp.reverse() };
962 }
963 }
965
966 std::cmp::Ordering::Equal
968 });
969
970 if let Some((primary_col, ascending)) = sort_columns.first() {
972 if let Some(visible_idx) = self.visible_columns.iter().position(|&x| x == *primary_col)
974 {
975 self.sort_state.column = Some(visible_idx);
976 self.sort_state.order = if *ascending {
977 SortOrder::Ascending
978 } else {
979 SortOrder::Descending
980 };
981 }
982 }
983
984 Ok(())
985 }
986
987 pub fn toggle_sort(&mut self, column_index: usize) -> Result<()> {
990 let source_column_index = if column_index < self.visible_columns.len() {
992 self.visible_columns[column_index]
993 } else {
994 return Err(anyhow::anyhow!(
995 "Column index {} out of bounds (visible columns: {})",
996 column_index,
997 self.visible_columns.len()
998 ));
999 };
1000
1001 let next_order = if self.sort_state.column == Some(column_index) {
1003 match self.sort_state.order {
1005 SortOrder::None => SortOrder::Ascending,
1006 SortOrder::Ascending => SortOrder::Descending,
1007 SortOrder::Descending => SortOrder::None,
1008 }
1009 } else {
1010 SortOrder::Ascending
1012 };
1013
1014 match next_order {
1016 SortOrder::Ascending => {
1017 self.apply_sort_internal(source_column_index, true)?;
1018 self.sort_state.column = Some(column_index);
1020 self.sort_state.order = SortOrder::Ascending;
1021 }
1022 SortOrder::Descending => {
1023 self.apply_sort_internal(source_column_index, false)?;
1024 self.sort_state.column = Some(column_index);
1025 self.sort_state.order = SortOrder::Descending;
1026 }
1027 SortOrder::None => {
1028 self.sort_state.column = None;
1029 self.sort_state.order = SortOrder::None;
1030 self.clear_sort();
1031 }
1032 }
1033
1034 Ok(())
1035 }
1036
1037 pub fn get_sort_state(&self) -> &SortState {
1039 &self.sort_state
1040 }
1041
1042 pub fn get_visible_column_indices(&self) -> Vec<usize> {
1045 self.visible_columns.clone()
1046 }
1047
1048 pub fn clear_sort(&mut self) {
1050 self.sort_state.column = None;
1052 self.sort_state.order = SortOrder::None;
1053
1054 self.visible_rows = self.base_rows.clone();
1057
1058 if let Some(pattern) = self.filter_pattern.clone() {
1060 let case_insensitive = false; self.apply_text_filter(&pattern, case_insensitive);
1062 }
1063 }
1064
1065 pub fn add_virtual_column(&mut self, virtual_column: VirtualColumn) {
1069 self.virtual_columns.push(virtual_column);
1070 }
1071
1072 pub fn add_row_numbers(&mut self, position: VirtualColumnPosition) {
1074 let row_num_column = VirtualColumn {
1075 name: "#".to_string(),
1076 generator: Arc::new(|row_index| format!("{}", row_index + 1)),
1077 width: Some(4), position,
1079 };
1080 self.add_virtual_column(row_num_column);
1081 }
1082
1083 pub fn remove_virtual_columns(&mut self, name: &str) {
1085 self.virtual_columns.retain(|col| col.name != name);
1086 }
1087
1088 pub fn toggle_row_numbers(&mut self) {
1090 if self.virtual_columns.iter().any(|col| col.name == "#") {
1091 self.remove_virtual_columns("#");
1092 } else {
1093 self.add_row_numbers(VirtualColumnPosition::Left);
1094 }
1095 }
1096
1097 pub fn has_row_numbers(&self) -> bool {
1099 self.virtual_columns.iter().any(|col| col.name == "#")
1100 }
1101
1102 pub fn get_all_column_names(&self) -> Vec<String> {
1104 let mut result = Vec::new();
1105 let all_source_names = self.source.column_names();
1106 let real_column_names: Vec<String> = self
1108 .get_display_columns()
1109 .iter()
1110 .map(|&i| {
1111 all_source_names
1112 .get(i)
1113 .cloned()
1114 .unwrap_or_else(|| format!("col_{}", i))
1115 })
1116 .collect();
1117
1118 let mut virtual_left = Vec::new();
1120 let mut virtual_right = Vec::new();
1121 let mut virtual_indexed = Vec::new();
1122
1123 for vcol in &self.virtual_columns {
1124 match vcol.position {
1125 VirtualColumnPosition::Left => virtual_left.push(vcol.name.clone()),
1126 VirtualColumnPosition::Right => virtual_right.push(vcol.name.clone()),
1127 VirtualColumnPosition::Index(idx) => virtual_indexed.push((idx, vcol.name.clone())),
1128 }
1129 }
1130
1131 result.extend(virtual_left);
1133
1134 for (i, real_name) in real_column_names.into_iter().enumerate() {
1136 for (idx, vname) in &virtual_indexed {
1138 if *idx == i {
1139 result.push(vname.clone());
1140 }
1141 }
1142 result.push(real_name);
1143 }
1144
1145 result.extend(virtual_right);
1147
1148 result
1149 }
1150
1151 pub fn row_count(&self) -> usize {
1153 let count = self.visible_rows.len();
1154
1155 if let Some(limit) = self.limit {
1157 let available = count.saturating_sub(self.offset);
1158 available.min(limit)
1159 } else {
1160 count.saturating_sub(self.offset)
1161 }
1162 }
1163
1164 pub fn column_count(&self) -> usize {
1166 self.visible_columns.len() + self.virtual_columns.len()
1168 }
1169
1170 pub fn column_names(&self) -> Vec<String> {
1172 self.get_all_column_names()
1173 }
1174
1175 pub fn get_row(&self, index: usize) -> Option<DataRow> {
1177 let actual_index = index + self.offset;
1178
1179 if let Some(limit) = self.limit {
1181 if index >= limit {
1182 return None;
1183 }
1184 }
1185
1186 let row_idx = *self.visible_rows.get(actual_index)?;
1188
1189 let mut values = Vec::new();
1191
1192 let mut real_values = Vec::new();
1194 for &col_idx in self.get_display_columns().iter() {
1195 let value = self
1196 .source
1197 .get_value(row_idx, col_idx)
1198 .cloned()
1199 .unwrap_or(DataValue::Null);
1200 real_values.push(value);
1201 }
1202
1203 let mut virtual_left = Vec::new();
1205 let mut virtual_right = Vec::new();
1206 let mut virtual_indexed = Vec::new();
1207
1208 for vcol in &self.virtual_columns {
1209 let virtual_value = DataValue::String((vcol.generator)(row_idx));
1210 match vcol.position {
1211 VirtualColumnPosition::Left => virtual_left.push(virtual_value),
1212 VirtualColumnPosition::Right => virtual_right.push(virtual_value),
1213 VirtualColumnPosition::Index(idx) => virtual_indexed.push((idx, virtual_value)),
1214 }
1215 }
1216
1217 values.extend(virtual_left);
1219
1220 for (i, real_value) in real_values.into_iter().enumerate() {
1222 for (idx, vvalue) in &virtual_indexed {
1224 if *idx == i {
1225 values.push(vvalue.clone());
1226 }
1227 }
1228 values.push(real_value);
1229 }
1230
1231 values.extend(virtual_right);
1233
1234 Some(DataRow::new(values))
1235 }
1236
1237 pub fn get_rows(&self) -> Vec<DataRow> {
1239 let count = self.row_count();
1240 (0..count).filter_map(|i| self.get_row(i)).collect()
1241 }
1242
1243 pub fn source(&self) -> &DataTable {
1245 &self.source
1246 }
1247
1248 pub fn source_arc(&self) -> Arc<DataTable> {
1250 Arc::clone(&self.source)
1251 }
1252
1253 pub fn is_column_visible(&self, index: usize) -> bool {
1255 self.pinned_columns.contains(&index) || self.visible_columns.contains(&index)
1256 }
1257
1258 pub fn visible_column_indices(&self) -> &[usize] {
1260 &self.visible_columns
1261 }
1262
1263 pub fn display_column_indices(&self) -> Vec<usize> {
1265 self.get_display_columns()
1266 }
1267
1268 pub fn visible_row_indices(&self) -> &[usize] {
1270 &self.visible_rows
1271 }
1272
1273 pub fn shrink_to_fit(&mut self) {
1275 self.visible_rows.shrink_to_fit();
1276 self.visible_columns.shrink_to_fit();
1277 self.pinned_columns.shrink_to_fit();
1278 self.base_rows.shrink_to_fit();
1279 self.base_columns.shrink_to_fit();
1280 self.matching_columns.shrink_to_fit();
1281 self.virtual_columns.shrink_to_fit();
1282 }
1283
1284 pub fn search_columns(&mut self, pattern: &str) {
1288 self.column_search_pattern = if pattern.is_empty() {
1289 None
1290 } else {
1291 Some(pattern.to_string())
1292 };
1293
1294 if pattern.is_empty() {
1295 self.matching_columns.clear();
1296 self.current_column_match = 0;
1297 return;
1298 }
1299
1300 let pattern_lower = pattern.to_lowercase();
1302 self.matching_columns = self
1303 .visible_columns
1304 .iter()
1305 .enumerate()
1306 .filter_map(|(visible_idx, &source_idx)| {
1307 let col_name = &self.source.columns[source_idx].name;
1308 if col_name.to_lowercase().contains(&pattern_lower) {
1309 debug!(target: "column_search",
1310 "Found match: '{}' at visible_idx={}, source_idx={}",
1311 col_name, visible_idx, source_idx);
1312 Some((visible_idx, col_name.clone()))
1313 } else {
1314 None
1315 }
1316 })
1317 .collect();
1318
1319 debug!(target: "column_search",
1320 "Total matches found: {}, visible_columns.len()={}, pattern='{}'",
1321 self.matching_columns.len(), self.visible_columns.len(), pattern);
1322
1323 self.current_column_match = 0;
1325 }
1326
1327 pub fn clear_column_search(&mut self) {
1329 self.column_search_pattern = None;
1330 self.matching_columns.clear();
1331 self.current_column_match = 0;
1332 }
1333
1334 pub fn next_column_match(&mut self) -> Option<usize> {
1336 if self.matching_columns.is_empty() {
1337 return None;
1338 }
1339
1340 self.current_column_match = (self.current_column_match + 1) % self.matching_columns.len();
1341 Some(self.matching_columns[self.current_column_match].0)
1342 }
1343
1344 pub fn prev_column_match(&mut self) -> Option<usize> {
1346 if self.matching_columns.is_empty() {
1347 return None;
1348 }
1349
1350 if self.current_column_match == 0 {
1351 self.current_column_match = self.matching_columns.len() - 1;
1352 } else {
1353 self.current_column_match -= 1;
1354 }
1355 Some(self.matching_columns[self.current_column_match].0)
1356 }
1357
1358 pub fn column_search_pattern(&self) -> Option<&str> {
1360 self.column_search_pattern.as_deref()
1361 }
1362
1363 pub fn get_matching_columns(&self) -> &[(usize, String)] {
1365 &self.matching_columns
1366 }
1367
1368 pub fn current_column_match_index(&self) -> usize {
1370 self.current_column_match
1371 }
1372
1373 pub fn get_current_column_match(&self) -> Option<usize> {
1375 if self.matching_columns.is_empty() {
1376 None
1377 } else {
1378 Some(self.matching_columns[self.current_column_match].0)
1379 }
1380 }
1381
1382 pub fn has_column_search(&self) -> bool {
1384 self.column_search_pattern.is_some()
1385 }
1386
1387 fn get_real_column_names(&self) -> Vec<String> {
1389 let all_source_names = self.source.column_names();
1390 let display_columns = self.get_display_columns();
1391
1392 display_columns
1393 .iter()
1394 .filter_map(|&idx| all_source_names.get(idx).cloned())
1395 .collect()
1396 }
1397
1398 fn extract_real_values_from_row(&self, full_row: &DataRow) -> Vec<DataValue> {
1400 let mut real_values = Vec::new();
1401 let mut value_idx = 0;
1402
1403 let left_virtual_count = self
1405 .virtual_columns
1406 .iter()
1407 .filter(|vc| matches!(vc.position, VirtualColumnPosition::Left))
1408 .count();
1409
1410 value_idx += left_virtual_count;
1412
1413 let real_column_count = self.get_display_columns().len();
1415 for _ in 0..real_column_count {
1416 if value_idx < full_row.values.len() {
1417 real_values.push(full_row.values[value_idx].clone());
1418 value_idx += 1;
1419 }
1420 }
1421
1422 real_values
1423 }
1424
1425 pub fn to_json(&self) -> Value {
1428 let column_names = self.get_real_column_names();
1430 let mut rows = Vec::new();
1431
1432 for row_idx in 0..self.row_count() {
1434 if let Some(full_row) = self.get_row(row_idx) {
1435 let real_values = self.extract_real_values_from_row(&full_row);
1437
1438 let mut obj = serde_json::Map::new();
1439 for (col_idx, col_name) in column_names.iter().enumerate() {
1440 if let Some(value) = real_values.get(col_idx) {
1441 let json_value = match value {
1442 DataValue::String(s) => json!(s),
1443 DataValue::InternedString(s) => json!(s.as_ref()),
1444 DataValue::Integer(i) => json!(i),
1445 DataValue::Float(f) => json!(f),
1446 DataValue::Boolean(b) => json!(b),
1447 DataValue::DateTime(dt) => json!(dt),
1448 DataValue::Null => json!(null),
1449 };
1450 obj.insert(col_name.clone(), json_value);
1451 }
1452 }
1453 rows.push(json!(obj));
1454 }
1455 }
1456
1457 json!(rows)
1458 }
1459
1460 pub fn to_csv(&self) -> Result<String> {
1462 let mut csv_output = String::new();
1463 let column_names = self.get_real_column_names();
1465
1466 csv_output.push_str(&column_names.join(","));
1468 csv_output.push('\n');
1469
1470 for row_idx in 0..self.row_count() {
1472 if let Some(full_row) = self.get_row(row_idx) {
1473 let real_values = self.extract_real_values_from_row(&full_row);
1475
1476 let row_strings: Vec<String> = real_values
1477 .iter()
1478 .map(|v| {
1479 let s = v.to_string();
1480 if s.contains(',') || s.contains('"') || s.contains('\n') {
1482 format!("\"{}\"", s.replace('"', "\"\""))
1483 } else {
1484 s
1485 }
1486 })
1487 .collect();
1488 csv_output.push_str(&row_strings.join(","));
1489 csv_output.push('\n');
1490 }
1491 }
1492
1493 Ok(csv_output)
1494 }
1495
1496 pub fn to_tsv(&self) -> Result<String> {
1498 let mut tsv_output = String::new();
1499 let column_names = self.get_real_column_names();
1501
1502 tsv_output.push_str(&column_names.join("\t"));
1504 tsv_output.push('\n');
1505
1506 for row_idx in 0..self.row_count() {
1508 if let Some(full_row) = self.get_row(row_idx) {
1509 let real_values = self.extract_real_values_from_row(&full_row);
1511
1512 let row_strings: Vec<String> = real_values.iter().map(|v| v.to_string()).collect();
1513 tsv_output.push_str(&row_strings.join("\t"));
1514 tsv_output.push('\n');
1515 }
1516 }
1517
1518 Ok(tsv_output)
1519 }
1520
1521 pub fn get_column_values(&self, column_index: usize) -> Vec<String> {
1523 use tracing::trace;
1524
1525 let mut values = Vec::new();
1526 let row_count = self.row_count();
1527
1528 trace!(
1529 "get_column_values: Getting column {} values from {} visible rows",
1530 column_index,
1531 row_count
1532 );
1533
1534 for row_idx in 0..row_count {
1535 if let Some(row) = self.get_row(row_idx) {
1537 if let Some(value) = row.values.get(column_index) {
1540 let str_value = value
1541 .to_string()
1542 .replace('\t', " ")
1543 .replace('\n', " ")
1544 .replace('\r', "");
1545 values.push(str_value);
1546 } else {
1547 values.push("NULL".to_string());
1548 }
1549 }
1550 }
1551
1552 trace!("get_column_values: Retrieved {} values", values.len());
1553 values
1554 }
1555
1556 pub fn get_cell_value(&self, row_index: usize, column_index: usize) -> Option<String> {
1558 if let Some(row) = self.get_row(row_index) {
1560 row.values.get(column_index).map(|v| v.to_string())
1563 } else {
1564 None
1565 }
1566 }
1567
1568 pub fn get_row_values(&self, row_index: usize) -> Option<Vec<String>> {
1570 self.get_row(row_index)
1571 .map(|row| row.values.iter().map(|v| v.to_string()).collect())
1572 }
1573
1574 pub fn get_row_visual_values(&self, row_index: usize) -> Option<Vec<String>> {
1577 if let Some(row) = self.get_row(row_index) {
1578 let values: Vec<String> = row.values.iter().map(|v| v.to_string()).collect();
1581 Some(values)
1582 } else {
1583 None
1584 }
1585 }
1586
1587 pub fn get_column_index_mapping(&self) -> Vec<(usize, String, usize)> {
1590 let mut mappings = Vec::new();
1591
1592 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1593 if let Some(column) = self.source.columns.get(datatable_idx) {
1594 mappings.push((visible_idx, column.name.clone(), datatable_idx));
1595 }
1596 }
1597
1598 mappings
1599 }
1600
1601 pub fn get_column_debug_info(&self) -> String {
1603 let mut info = String::new();
1604 info.push_str("Column Mapping (Visible â DataTable):\n");
1605
1606 let total_columns = self.source.columns.len();
1607 let visible_count = self.visible_columns.len();
1608 let hidden_count = total_columns - visible_count;
1609
1610 info.push_str(&format!(
1611 "Total: {} columns, Visible: {}, Hidden: {}\n\n",
1612 total_columns, visible_count, hidden_count
1613 ));
1614
1615 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1617 if let Some(column) = self.source.columns.get(datatable_idx) {
1618 let pinned_marker = if self.pinned_columns.contains(&datatable_idx) {
1619 " [PINNED]"
1620 } else {
1621 ""
1622 };
1623 info.push_str(&format!(
1624 " V[{:3}] â DT[{:3}] : {}{}\n",
1625 visible_idx, datatable_idx, column.name, pinned_marker
1626 ));
1627 }
1628 }
1629
1630 if hidden_count > 0 {
1632 info.push_str("\nHidden Columns:\n");
1633 for (idx, column) in self.source.columns.iter().enumerate() {
1634 if !self.visible_columns.contains(&idx) {
1635 info.push_str(&format!(" DT[{:3}] : {}\n", idx, column.name));
1636 }
1637 }
1638 }
1639
1640 if !self.pinned_columns.is_empty() {
1642 info.push_str(&format!("\nPinned Columns: {:?}\n", self.pinned_columns));
1643 }
1644
1645 let is_reordered = self.visible_columns.windows(2).any(|w| w[0] > w[1]);
1647
1648 if is_reordered {
1649 info.push_str("\nâ ď¸ Column order has been modified from original DataTable order\n");
1650 }
1651
1652 info
1653 }
1654}
1655
1656impl DataProvider for DataView {
1659 fn get_row(&self, index: usize) -> Option<Vec<String>> {
1660 self.get_row(index)
1661 .map(|row| row.values.iter().map(|v| v.to_string()).collect())
1662 }
1663
1664 fn get_column_names(&self) -> Vec<String> {
1665 self.column_names()
1666 }
1667
1668 fn get_row_count(&self) -> usize {
1669 self.row_count()
1670 }
1671
1672 fn get_column_count(&self) -> usize {
1673 self.column_count()
1674 }
1675
1676 fn get_column_widths(&self) -> Vec<usize> {
1677 let mut widths = vec![0; self.column_count()];
1679
1680 for (i, name) in self.column_names().iter().enumerate() {
1682 widths[i] = name.len();
1683 }
1684
1685 let sample_size = 100.min(self.row_count());
1688 for row_idx in 0..sample_size {
1689 if let Some(row) = self.get_row(row_idx) {
1690 for (col_idx, value) in row.values.iter().enumerate() {
1691 if col_idx < widths.len() {
1692 let display_len = value.to_string().len();
1693 widths[col_idx] = widths[col_idx].max(display_len);
1694 }
1695 }
1696 }
1697 }
1698
1699 let terminal_width = crossterm::terminal::size()
1701 .map(|(w, _)| w as usize)
1702 .unwrap_or(120);
1703
1704 let available_width = terminal_width.saturating_sub(10);
1707 let visible_cols = self.visible_columns.len().min(10);
1708
1709 let dynamic_max = if visible_cols > 0 {
1711 (available_width / visible_cols).min(80).max(20)
1712 } else {
1713 40
1714 };
1715
1716 for width in &mut widths {
1718 *width = (*width).clamp(6, dynamic_max);
1719 }
1720
1721 widths
1722 }
1723}
1724
1725impl std::fmt::Debug for DataView {
1727 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1728 f.debug_struct("DataView")
1729 .field("source_name", &self.source.name)
1730 .field("visible_rows", &self.visible_rows.len())
1731 .field("visible_columns", &self.visible_columns.len())
1732 .field("has_filter", &self.filter_pattern.is_some())
1733 .field("has_column_search", &self.column_search_pattern.is_some())
1734 .finish()
1735 }
1736}
1737
1738#[cfg(test)]
1739mod tests {
1740 use super::*;
1741 use crate::data::datatable::{DataColumn, DataRow, DataTable, DataValue};
1742 use std::sync::Arc;
1743
1744 #[allow(dead_code)]
1747 fn test_hide_empty_columns_index_fix() {
1748 let mut table = DataTable::new("test");
1750
1751 table.add_column(DataColumn::new("name"));
1753 table.add_column(DataColumn::new("empty1")); table.add_column(DataColumn::new("salary"));
1755 table.add_column(DataColumn::new("empty2")); table.add_column(DataColumn::new("department"));
1757
1758 table
1760 .add_row(DataRow::new(vec![
1761 DataValue::String("John".to_string()),
1762 DataValue::Null,
1763 DataValue::Integer(50000),
1764 DataValue::Null,
1765 DataValue::String("Engineering".to_string()),
1766 ]))
1767 .unwrap();
1768
1769 table
1770 .add_row(DataRow::new(vec![
1771 DataValue::String("Jane".to_string()),
1772 DataValue::Null,
1773 DataValue::Integer(60000),
1774 DataValue::Null,
1775 DataValue::String("Marketing".to_string()),
1776 ]))
1777 .unwrap();
1778
1779 let mut dataview = DataView::new(Arc::new(table));
1781
1782 assert_eq!(dataview.column_count(), 5);
1784
1785 let hidden_count = dataview.hide_empty_columns();
1787
1788 assert_eq!(hidden_count, 2);
1790
1791 assert_eq!(dataview.column_count(), 3);
1793
1794 let final_columns = dataview.column_names();
1796
1797 assert_eq!(final_columns[0], "name");
1799 assert_eq!(final_columns[1], "salary");
1800 assert_eq!(final_columns[2], "department");
1801
1802 let hidden_columns = dataview.get_hidden_column_names();
1804 assert!(hidden_columns.contains(&"empty1".to_string()));
1805 assert!(hidden_columns.contains(&"empty2".to_string()));
1806 assert_eq!(hidden_columns.len(), 2);
1807 }
1808}