gitql_cli/printer/
table_printer.rs1use 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 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 if !self.pagination || self.page_size >= group_len {
44 print_group_as_table(titles, table_headers, &group.rows);
45 return;
46 }
47
48 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 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 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 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}