datafusion_dft/tui/ui/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18pub mod convert;
19pub mod tabs;
20
21use ratatui::{prelude::*, style::palette::tailwind};
22use strum::{Display, EnumIter, FromRepr};
23
24use crate::tui::App;
25
26use self::tabs::{context, history, logs, sql};
27
28#[derive(Clone, Copy, Debug, Display, FromRepr, EnumIter)]
29pub enum SelectedTab {
30    #[allow(clippy::upper_case_acronyms)]
31    #[strum(to_string = "SQL")]
32    SQL,
33    #[cfg(feature = "flightsql")]
34    #[strum(to_string = "FlightSQL")]
35    FlightSQL,
36    #[strum(to_string = "History")]
37    History,
38    #[strum(to_string = "Logs")]
39    Logs,
40    #[strum(to_string = "Context")]
41    Context,
42}
43
44impl SelectedTab {
45    pub fn title(self, _app: &App) -> Line<'static> {
46        let padding = Span::from("  ");
47        match self {
48            SelectedTab::SQL => {
49                let title = Span::from("SQL (1)").bold();
50                Line::from_iter(vec![padding.clone(), title, padding.clone()])
51                    .fg(tailwind::SLATE.c200)
52                    .bg(self.bg())
53            }
54            #[cfg(feature = "flightsql")]
55            Self::FlightSQL => {
56                let status = _app.state.flightsql_tab.connection_status().tab_display();
57                let title_text = format!("FlightSQL{status} (2)");
58                let title = Span::from(title_text).bold();
59                Line::from_iter(vec![padding.clone(), title, padding.clone()])
60                    .fg(tailwind::SLATE.c200)
61                    .bg(self.bg())
62            }
63            Self::Logs => {
64                #[cfg(feature = "flightsql")]
65                let title = Span::from("LOGS (4)").bold();
66
67                #[cfg(not(feature = "flightsql"))]
68                let title = Span::from("LOGS (3)").bold();
69
70                Line::from_iter(vec![padding.clone(), title, padding.clone()])
71                    .fg(tailwind::SLATE.c200)
72                    .bg(self.bg())
73            }
74            Self::Context => {
75                #[cfg(feature = "flightsql")]
76                let title = Span::from("CONTEXT (5)").bold();
77
78                #[cfg(not(feature = "flightsql"))]
79                let title = Span::from("CONTEXT (4)").bold();
80
81                Line::from_iter(vec![padding.clone(), title, padding.clone()])
82                    .fg(tailwind::SLATE.c200)
83                    .bg(self.bg())
84            }
85            Self::History => {
86                #[cfg(feature = "flightsql")]
87                let title = Span::from("HISTORY (3)").bold();
88                #[cfg(not(feature = "flightsql"))]
89                let title = Span::from("HISTORY (2)").bold();
90                Line::from_iter(vec![padding.clone(), title, padding.clone()])
91                    .fg(tailwind::SLATE.c200)
92                    .bg(self.bg())
93            }
94        }
95    }
96
97    const fn bg(self) -> Color {
98        match self {
99            Self::SQL => tailwind::ORANGE.c700,
100            Self::Logs => tailwind::ORANGE.c700,
101            Self::Context => tailwind::ORANGE.c700,
102            Self::History => tailwind::ORANGE.c700,
103            #[cfg(feature = "flightsql")]
104            Self::FlightSQL => tailwind::ORANGE.c700,
105        }
106    }
107
108    /// Get the previous tab, if there is no previous tab return the current tab.
109    pub fn previous(self) -> Self {
110        let current_index: usize = self as usize;
111        let previous_index = current_index.saturating_sub(1);
112        Self::from_repr(previous_index).unwrap_or(self)
113    }
114
115    /// Get the next tab, if there is no next tab return the current tab.
116    pub fn next(self) -> Self {
117        let current_index = self as usize;
118        let next_index = current_index.saturating_add(1);
119        Self::from_repr(next_index).unwrap_or(self)
120    }
121
122    fn render_sql(self, area: Rect, buf: &mut Buffer, app: &App) {
123        sql::render_sql(area, buf, app)
124    }
125
126    fn render_logs(self, area: Rect, buf: &mut Buffer, app: &App) {
127        logs::render_logs(area, buf, app)
128    }
129
130    fn render_context(self, area: Rect, buf: &mut Buffer, app: &App) {
131        context::render_context(area, buf, app)
132    }
133
134    fn render_history(self, area: Rect, buf: &mut Buffer, app: &App) {
135        history::render_history(area, buf, app)
136    }
137
138    #[cfg(feature = "flightsql")]
139    fn render_flightsql(self, area: Rect, buf: &mut Buffer, app: &App) {
140        use self::tabs::flightsql;
141
142        flightsql::render_sql(area, buf, app)
143    }
144
145    /// Render the tab with the provided state.
146    ///
147    /// This used to be an impl of `Widget` but we weren't able to pass state
148    /// as a paramter to the render method so moved to impl on the SelectedTab.
149    /// It's not clear if this will have future impact.
150    pub fn render(self, area: Rect, buf: &mut Buffer, app: &App) {
151        match self {
152            Self::SQL => self.render_sql(area, buf, app),
153            Self::Logs => self.render_logs(area, buf, app),
154            Self::Context => self.render_context(area, buf, app),
155            Self::History => self.render_history(area, buf, app),
156            #[cfg(feature = "flightsql")]
157            Self::FlightSQL => self.render_flightsql(area, buf, app),
158        }
159    }
160}