gitql_cli/printer/
table_printer.rs

1use gitql_core::object::Row;
2
3use super::base::OutputPrinter;
4
5enum PaginationInput {
6    NextPage,
7    PreviousPage,
8    Quit,
9}
10
11pub struct TablePrinter {
12    pub pagination: bool,
13    pub page_size: usize,
14}
15
16impl TablePrinter {
17    pub fn new(pagination: bool, page_size: usize) -> Self {
18        TablePrinter {
19            pagination,
20            page_size,
21        }
22    }
23}
24
25impl OutputPrinter for TablePrinter {
26    fn print(&self, object: &mut gitql_core::object::GitQLObject) {
27        if object.is_empty() || object.groups[0].is_empty() {
28            return;
29        }
30
31        let titles = &object.titles;
32        let group = object.groups.first().unwrap();
33        let group_len = group.len();
34
35        // Setup table headers
36        let header_color = comfy_table::Color::Green;
37        let mut table_headers = vec![];
38        for key in titles {
39            table_headers.push(comfy_table::Cell::new(key).fg(header_color));
40        }
41
42        // Print all data without pagination
43        if !self.pagination || self.page_size >= group_len {
44            print_group_as_table(titles, table_headers, &group.rows);
45            return;
46        }
47
48        // Setup the pagination mode
49        let number_of_pages = (group_len as f64 / self.page_size as f64).ceil() as usize;
50        let mut current_page = 1;
51
52        loop {
53            let start_index = (current_page - 1) * self.page_size;
54            let end_index = (start_index + self.page_size).min(group_len);
55
56            let current_page_groups = &group.rows[start_index..end_index];
57            println!("Page {}/{}", current_page, number_of_pages);
58            print_group_as_table(titles, table_headers.clone(), current_page_groups);
59
60            let pagination_input = handle_pagination_input(current_page, number_of_pages);
61            match pagination_input {
62                PaginationInput::NextPage => current_page += 1,
63                PaginationInput::PreviousPage => current_page -= 1,
64                PaginationInput::Quit => break,
65            }
66        }
67    }
68}
69
70fn print_group_as_table(titles: &[String], table_headers: Vec<comfy_table::Cell>, rows: &[Row]) {
71    let mut table = comfy_table::Table::new();
72
73    // Setup table style
74    table.load_preset(comfy_table::presets::UTF8_FULL);
75    table.apply_modifier(comfy_table::modifiers::UTF8_ROUND_CORNERS);
76    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
77
78    table.set_header(table_headers);
79
80    let titles_len = titles.len();
81
82    // Add rows to the table
83    for row in rows {
84        let mut table_row: Vec<comfy_table::Cell> = vec![];
85        for index in 0..titles_len {
86            if let Some(value) = row.values.get(index) {
87                table_row.push(comfy_table::Cell::new(value.literal()));
88            }
89        }
90        table.add_row(table_row);
91    }
92
93    // Print table
94    println!("{table}");
95}
96
97fn handle_pagination_input(current_page: usize, number_of_pages: usize) -> PaginationInput {
98    loop {
99        if current_page < 2 {
100            println!("Enter 'n' for next page, or 'q' to quit:");
101        } else if current_page == number_of_pages {
102            println!("'p' for previous page, or 'q' to quit:");
103        } else {
104            println!("Enter 'n' for next page, 'p' for previous page, or 'q' to quit:");
105        }
106
107        std::io::Write::flush(&mut std::io::stdout()).expect("flush failed!");
108
109        let mut line = String::new();
110        std::io::stdin()
111            .read_line(&mut line)
112            .expect("Failed to read input");
113
114        let input = line.trim();
115        if input == "q" || input == "n" || input == "p" {
116            match input {
117                "n" => {
118                    if current_page < number_of_pages {
119                        return PaginationInput::NextPage;
120                    } else {
121                        println!("Already on the last page");
122                        continue;
123                    }
124                }
125                "p" => {
126                    if current_page > 1 {
127                        return PaginationInput::PreviousPage;
128                    } else {
129                        println!("Already on the first page");
130                        continue;
131                    }
132                }
133                "q" => return PaginationInput::Quit,
134                _ => unreachable!(),
135            }
136        }
137
138        println!("Invalid input");
139    }
140}