rsnaker/graphics/menus/retro_parameter_table/
customized_with_edit.rs1use crate::game_logic::game_options::{GameOptions, ONLY_FOR_CLI_PARAMETERS, get_parameter_range};
2use crate::game_logic::logger::log_configuration::LogLevel;
3use crate::graphics::menus::retro_parameter_table::generic_logic::{
4 ActionInputs, CellValue, FooterData, GenericMenu, RowData, TableParameterAction,
5 get_default_action_input,
6};
7use clap::{CommandFactory, ValueEnum};
8use crossterm::event::KeyCode;
9use ratatui::DefaultTerminal;
10
11pub fn setup_and_run_cli_table_parameters(
12 terminal: &mut DefaultTerminal,
13 options: &mut GameOptions,
14) {
15 let current_preset = options.load;
16 let data: Vec<RowData> = load_parameter_cli_in_table(options);
17 let mut actions = get_default_action_input();
18 actions.push(ActionInputs {
19 key: vec![KeyCode::Char('x'), KeyCode::Char('X'), KeyCode::End],
20 action: vec![
21 TableParameterAction::ApplyAndSave(options),
22 TableParameterAction::Quit,
23 ],
24 });
25 actions.push(ActionInputs {
26 key: vec![KeyCode::Char('r'), KeyCode::Char('R')],
27 action: vec![TableParameterAction::Randomize(
28 random_parameter_cli_in_table,
29 )],
30 });
31 for i in 1..=7u16 {
33 let key_char = char::from_digit(u32::from(i), 10).unwrap();
37
38 actions.push(ActionInputs {
39 key: vec![
41 KeyCode::Char(key_char),
42 KeyCode::Char(key_char.to_ascii_uppercase()),
43 ],
44 action: vec![TableParameterAction::LoadPreset(i, |preset| {
46 let footer_updated_data = Some(parameters_cli_get_footer_data(Some(preset)));
47 if let Ok(game_options_from_preset) =
49 &mut GameOptions::load_from_toml_preset(preset)
50 {
51 (
52 Some(load_parameter_cli_in_table(game_options_from_preset)),
53 footer_updated_data,
54 )
55 } else {
56 (None, footer_updated_data)
58 }
59 })],
60 });
61 }
62 GenericMenu::new(
64 data,
65 ¶meters_cli_get_headers(),
66 parameters_cli_get_footer_data(current_preset),
67 current_preset,
68 )
69 .run(actions, terminal);
70}
71
72#[must_use]
73fn load_parameter_cli_in_table(options: &mut GameOptions) -> Vec<RowData> {
74 let cmd = GameOptions::command();
75 let mut rows = vec![];
76 let mut arg_value;
77 for arg in cmd.get_arguments().filter(|arg| {
80 !ONLY_FOR_CLI_PARAMETERS
81 .iter()
82 .any(|arg_pattern| arg.get_long().unwrap().contains(arg_pattern))
83 }) {
84 let mut all_value_for_this_arg = vec![];
86 let pv_bool_enum = arg.get_possible_values();
89 if pv_bool_enum.is_empty() {
90 if let Some(range) = get_parameter_range(arg.get_long().unwrap()) {
91 all_value_for_this_arg.extend(range.map(|i| i.to_string()));
92 } else {
93 all_value_for_this_arg.extend(GameOptions::emojis_iterator());
97 add_unique_symbol(&mut all_value_for_this_arg, options.head_symbol.clone());
99 add_unique_symbol(&mut all_value_for_this_arg, options.body_symbol.clone());
100 }
101 } else {
102 all_value_for_this_arg
104 .extend(pv_bool_enum.into_iter().map(|v| v.get_name().to_string()));
105 }
106 let mut index = 0;
110 if let Some(default_value) = options
112 .to_structured_toml()
113 .get(&arg.get_long().unwrap().replace('-', "_"))
114 {
115 let mut default_str = default_value.to_string();
116 if default_str.contains('"') {
118 default_str = default_str.split('"').collect::<Vec<&str>>()[1].to_string();
119 }
120 index = all_value_for_this_arg
121 .iter()
122 .position(|v| v.eq_ignore_ascii_case(&default_str))
123 .unwrap_or(0);
124 }
125 let arg_name = "--".to_string() + arg.get_long().unwrap();
127 arg_value = CellValue::new_with_options(arg_name, all_value_for_this_arg, index);
128
129 rows.push(RowData::new(vec![
130 arg_value,
131 CellValue::new(arg.get_long().unwrap().to_string()),
132 CellValue::new(
133 arg.get_help()
134 .unwrap_or_else(|| {
135 panic!("Missing help for argument: {}", arg.get_long().unwrap())
136 })
137 .to_string(),
138 ),
139 ]));
140 }
141 rows
142}
143
144#[must_use]
145fn random_parameter_cli_in_table(
146 current_preset: Option<u16>,
147 current_rows: &[RowData],
148) -> Vec<RowData> {
149 let mut random_options = GameOptions::random();
150 random_options.load = current_preset;
151 if let Some(log_level) = extract_log_level_from_rows(current_rows) {
152 random_options.log_level = log_level;
153 }
154 load_parameter_cli_in_table(&mut random_options)
155}
156
157fn extract_log_level_from_rows(rows: &[RowData]) -> Option<LogLevel> {
158 for row in rows {
159 for cell in &row.cells {
160 if let CellValue::Options {
161 option_name,
162 values,
163 index,
164 ..
165 } = cell
166 && option_name == "--log-level"
167 && let Some(val_str) = values.get(*index)
168 {
169 return LogLevel::from_str(val_str, true).ok();
170 }
171 }
172 }
173 None
174}
175
176#[must_use]
177fn parameters_cli_get_headers() -> Vec<String> {
178 vec![
179 "🎯 Value".to_string(),
180 "📋 Parameter".to_string(),
181 "📝 Description / super power".to_string(),
182 ]
183}
184#[must_use]
186pub fn parameters_cli_get_footer_data(current_preset: Option<u16>) -> Vec<FooterData> {
187 vec![
188 FooterData {
189 symbol: "Esc/Tab".into(),
190 text: "Quit".into(),
191 value: None,
192 },
193 FooterData {
194 symbol: "x/END".into(),
195 text: "Quit & Save".into(),
196 value: None,
197 },
198 FooterData {
199 symbol: "r".into(),
200 text: "Random".into(),
201 value: None,
202 },
203 FooterData {
204 symbol: "↕".into(),
205 text: "Move".into(),
206 value: None,
207 },
208 FooterData {
209 symbol: "← →".into(),
210 text: "Change value".into(),
211 value: None,
212 },
213 FooterData {
214 symbol: "1-7".into(),
215 text: "Load".into(),
216 value: current_preset,
217 },
218 ]
219}
220
221fn add_unique_symbol(collection: &mut Vec<String>, symbol: String) {
222 if !collection.contains(&symbol) {
223 collection.push(symbol);
224 }
225}