datui_lib/
table_switch.rs1use std::path::PathBuf;
7
8use crate::OpenOptions;
9use crate::widgets::datatable::DataTableState;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Table {
14 pub table: Option<String>,
16 pub label: String,
18 pub detail: String,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Tables {
25 pub file: PathBuf,
27 pub tables: Vec<Table>,
28 pub current: Option<usize>,
30}
31
32impl Tables {
33 pub fn several(&self) -> bool {
35 self.tables.len() > 1
36 }
37}
38
39pub fn of(state: &DataTableState, paths: &[PathBuf], options: &OpenOptions) -> Option<Tables> {
42 let [file] = paths else {
43 return None;
44 };
45 let file = file.clone();
46 if let Some(detail) = state.format_detail()
47 && !detail.tables.is_empty()
48 {
49 return Some(from_detail(file, detail, options.table.as_deref()));
50 }
51 if let Some(read) = state.format_read()
52 && !read.spec.is_delimited()
53 && read.spec.records.variants.len() > 1
54 {
55 return Some(record_types(file, &read.spec));
56 }
57 let splits = options.splits.as_deref()?;
58 let current = splits.split.clone()?;
59 if splits.others.is_empty() {
60 return None;
61 }
62 let mut names: Vec<String> = splits.others.clone();
63 names.push(current.clone());
64 names.sort();
65 names.dedup();
66 let at = names.iter().position(|n| *n == current);
67 Some(Tables {
68 file,
69 tables: names
70 .into_iter()
71 .map(|name| Table {
72 table: Some(name.clone()),
73 label: name,
74 detail: "split".to_string(),
75 })
76 .collect(),
77 current: at,
78 })
79}
80
81pub fn several(state: &DataTableState, paths: &[PathBuf], options: &OpenOptions) -> bool {
84 if paths.len() != 1 {
85 return false;
86 }
87 if let Some(detail) = state.format_detail()
88 && !detail.tables.is_empty()
89 {
90 return detail.tables.len() > 1;
91 }
92 if let Some(read) = state.format_read()
93 && !read.spec.is_delimited()
94 && read.spec.records.variants.len() > 1
95 {
96 return true;
97 }
98 options
99 .splits
100 .as_deref()
101 .is_some_and(|s| s.split.is_some() && s.others.iter().any(|o| Some(o) != s.split.as_ref()))
102}
103
104fn from_detail(file: PathBuf, detail: &crate::text_formats::Detail, asked: Option<&str>) -> Tables {
107 let said = |name: &str| {
108 detail
109 .list
110 .iter()
111 .find(|(key, _)| key == name)
112 .map(|(_, value)| match value {
113 crate::model_files::MetaValue::Text(text) => text
114 .split(", ")
115 .filter(|part| *part != "opened")
116 .collect::<Vec<_>>()
117 .join(", "),
118 _ => String::new(),
119 })
120 .unwrap_or_default()
121 };
122 let opened = detail.table.as_deref().or(asked);
123 let current = opened.and_then(|name| detail.tables.iter().position(|t| t == name));
124 Tables {
125 file,
126 tables: detail
127 .tables
128 .iter()
129 .map(|name| Table {
130 table: Some(name.clone()),
131 label: name.clone(),
132 detail: said(name),
133 })
134 .collect(),
135 current,
136 }
137}
138
139fn record_types(file: PathBuf, spec: &crate::formats::Spec) -> Tables {
142 let current = spec.variant.as_deref();
143 let whole = Table {
144 table: None,
145 label: file
146 .file_name()
147 .map(|n| n.to_string_lossy().into_owned())
148 .unwrap_or_else(|| file.display().to_string()),
149 detail: "every record type".to_string(),
150 };
151 let types = crate::members::variant_tables(spec)
152 .into_iter()
153 .map(|t| Table {
154 detail: crate::text_formats::count(t.columns.len() as u64, "column", "columns"),
155 table: Some(t.name.clone()),
156 label: t.name,
157 });
158 let tables: Vec<Table> = std::iter::once(whole).chain(types).collect();
159 let at = tables.iter().position(|t| t.table.as_deref() == current);
160 Tables {
161 file,
162 tables,
163 current: at,
164 }
165}