1use rand::rngs::ThreadRng;
2use std::{
3 collections::HashMap,
4 fmt,
5 fmt::{Debug, Display},
6 fs::File,
7 io,
8 io::prelude::*,
9 process,
10};
11
12const MAX_NUMBER_OF_BLOCKS: i32 = 12;
13const MIN_NUMBER_OF_BLOCKS: i32 = 2;
14const NUMBER_OF_LINES_TO_READ: i32 = 2;
15
16#[derive(Debug)]
17pub struct SadariEnvironment {
18 pub number_of_blocks: u8,
19 pub number_of_max_bridges: u8,
20 pub y_coordinate: u16,
21 rng: ThreadRng,
22 pub name_vec: Vec<String>,
23 pub result_vec: Vec<String>,
24 pub tick_rate: u64,
25}
26
27impl SadariEnvironment {
28 fn default() -> SadariEnvironment {
29 SadariEnvironment {
30 number_of_blocks: 0,
31 number_of_max_bridges: 6,
32 y_coordinate: 10,
33 rng: rand::thread_rng(),
34 name_vec: Vec::new(),
35 result_vec: Vec::new(),
36 tick_rate: 250,
37 }
38 }
39
40 fn number_of_blocks(mut self, number_of_blocks: u8) -> Self {
41 self.number_of_blocks = number_of_blocks;
42
43 self
44 }
45
46 fn name_vec(mut self, name_vec: Vec<String>) -> Self {
47 self.name_vec = name_vec;
48
49 self
50 }
51
52 fn result_vec(mut self, result_vec: Vec<String>) -> Self {
53 self.result_vec = result_vec;
54
55 self
56 }
57}
58
59impl Display for SadariEnvironment {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(
62 f,
63 "
64 sadari env, block : {}, \
65 max_bridges : {}, \
66 y_coordinate : {}, \
67 \nname_vec : {:?}, \
68 \nresult_vec : {:?}",
69 self.number_of_blocks,
70 self.number_of_max_bridges,
71 self.y_coordinate,
72 self.name_vec,
73 self.result_vec
74 )
75 }
76}
77
78mod interaction {
79 use super::{SadariEnvironment, MAX_NUMBER_OF_BLOCKS, MIN_NUMBER_OF_BLOCKS};
80 use std::io;
81 use std::io::prelude::*;
82
83 pub enum State {
84 Idle,
85 NameInput,
86 ResultInput,
87 BeforeDone,
88 Done,
89 Quit,
90 }
91
92 impl State {
93 fn next_state(self) -> State {
94 match self {
95 State::Idle => State::NameInput,
96 State::NameInput => State::ResultInput,
97 State::ResultInput => State::BeforeDone,
98 State::BeforeDone => State::Done,
99 _ => self,
100 }
101 }
102
103 fn previous_state(self) -> State {
104 match self {
105 State::NameInput => State::Idle,
106 State::ResultInput => State::NameInput,
107 State::BeforeDone => State::ResultInput,
108 _ => self,
109 }
110 }
111 }
112
113 pub fn help_guide() {
114 let text = r#"
115 There are TWO modes to run sadari application.
116 1. Using file path as input
117 2. Interacting with user by asking sevearl Questions.
118
119 1 -> For file as input mode example : cargo run ./text.txt
120 2 -> For interaction mode example : cargo run
121
122 Enjoy!
123 "#;
124
125 println!("{}", text);
126 }
127
128 fn idle_guide() {
129 println!("\tType list of names separated by comma! ex) name1, name2, name3 ...\n");
130 println!("\tQ,q) Quit\n");
131 print!("type: ");
132
133 io::stdout().flush().unwrap();
134 }
135
136 fn name_input_guide(v: &Vec<String>) {
137 println!("\tIs that right? \n\tnames: {:?}, len: {}\n", v, v.len());
138 println!("\tY, y) yes");
139 println!("\tN, n) no");
140 println!("\tQ,q) Quit\n");
141 print!("type: ");
142 io::stdout().flush().unwrap();
143 }
144
145 fn result_input_guide() {
146 println!("\tType list of results separated by comma! ex) result1, result2, result3 ...");
147 println!("\tR, r) If you want auto generated results");
148 println!("\tQ,q) Quit\n");
149 print!("type: ");
150 io::stdout().flush().unwrap();
151 }
152
153 fn before_done_guide(sadari_env: &SadariEnvironment) {
154 println!(
155 "\tIs that right? \n\tname: {:?}, len: {}",
156 sadari_env.name_vec,
157 sadari_env.name_vec.len()
158 );
159 println!(
160 "\tresult: {:?}, len: {}\n",
161 sadari_env.result_vec,
162 sadari_env.result_vec.len()
163 );
164 println!("\tY, y) yes");
165 println!("\tN, n) no");
166 println!("\tQ,q) Quit\n");
167 print!("type: ");
168 io::stdout().flush().unwrap();
169 }
170
171 pub fn handle_state_guide(state: &State, sadari_env: &SadariEnvironment) {
172 match state {
173 State::Idle => idle_guide(),
174 State::NameInput => name_input_guide(&sadari_env.name_vec),
175 State::ResultInput => result_input_guide(),
176 State::BeforeDone => before_done_guide(&sadari_env),
177 _ => {}
178 };
179 }
180
181 pub fn handle_user_input(
182 action: String,
183 state: State,
184 sadari_env: SadariEnvironment,
185 ) -> (SadariEnvironment, State) {
186 let action = action.trim();
187
188 let mut next_sadari_env = sadari_env;
189
190 match action {
191 "Q" | "q" => (next_sadari_env, State::Quit),
192 "Y" | "y" => {
193 let (is_valid, message) = validate_input(&state, &next_sadari_env);
194
195 match message {
196 Some(m) => println!("{}", m),
197 None => {}
198 };
199
200 let next_state = match state {
201 State::NameInput => {
202 if is_valid {
203 state.next_state()
204 } else {
205 state
206 }
207 }
208 State::BeforeDone => {
209 if is_valid {
210 state.next_state()
211 } else {
212 state.previous_state()
213 }
214 }
215 _ => state,
216 };
217
218 (next_sadari_env, next_state)
219 }
220 "N" | "n" => {
221 let next_state = match state {
222 State::NameInput => {
223 next_sadari_env = next_sadari_env.name_vec(Vec::new()).number_of_blocks(0);
224
225 state.previous_state()
226 }
227 State::BeforeDone => {
228 next_sadari_env = next_sadari_env.result_vec(Vec::new());
229
230 state.previous_state()
231 }
232 _ => state,
233 };
234
235 (next_sadari_env, next_state)
236 }
237 "R" | "r" => {
238 let next_state = match state {
239 State::ResultInput => {
240 let vec: Vec<String> = (0..next_sadari_env.number_of_blocks as u8)
241 .into_iter()
242 .map(|x| x.to_string())
243 .collect();
244
245 next_sadari_env = next_sadari_env.result_vec(vec);
246
247 state.next_state()
248 }
249 _ => state,
250 };
251
252 (next_sadari_env, next_state)
253 }
254 _ => {
255 let vec: Vec<String> = action
256 .split(",")
257 .map(move |x| String::from(x.trim()))
258 .collect();
259
260 let next_state = match state {
261 State::Idle => {
262 next_sadari_env = next_sadari_env
263 .number_of_blocks(vec.len() as u8)
264 .name_vec(vec);
265
266 state.next_state()
267 }
268 State::ResultInput => {
269 next_sadari_env = next_sadari_env.result_vec(vec);
270
271 let (is_valid, message) = validate_input(&state, &next_sadari_env);
272
273 match message {
274 Some(m) => println!("{}", m),
275 None => {}
276 };
277
278 if is_valid {
279 state.next_state()
280 } else {
281 next_sadari_env = next_sadari_env.result_vec(Vec::new());
282
283 state
284 }
285 }
286 _ => state,
287 };
288
289 (next_sadari_env, next_state)
290 }
291 }
292 }
293
294 fn validate_input(state: &State, sadari_env: &SadariEnvironment) -> (bool, Option<String>) {
295 match state {
296 State::NameInput => {
297 let len = sadari_env.name_vec.len();
298
299 if len >= MIN_NUMBER_OF_BLOCKS as usize && len <= MAX_NUMBER_OF_BLOCKS as usize {
300 (true, None)
301 } else {
302 (
303 false,
304 Some(format!(
305 "\n\tInput length should be {} <= input_length <= {}\n",
306 MIN_NUMBER_OF_BLOCKS, MAX_NUMBER_OF_BLOCKS
307 )),
308 )
309 }
310 }
311 State::BeforeDone | State::ResultInput => {
312 let name_vec_len = sadari_env.name_vec.len();
313 let result_vec_len = sadari_env.result_vec.len();
314
315 if name_vec_len != result_vec_len {
316 return (
317 false,
318 Some(format!(
319 "\n\tLengths are different! name: {}, result: {}\n",
320 name_vec_len, result_vec_len
321 )),
322 );
323 }
324
325 if result_vec_len >= MIN_NUMBER_OF_BLOCKS as usize
326 && result_vec_len <= MAX_NUMBER_OF_BLOCKS as usize
327 {
328 (true, None)
329 } else {
330 (
331 false,
332 Some(format!(
333 "\n\tInput length should be {} <= input_length <= {}\n",
334 MIN_NUMBER_OF_BLOCKS, MAX_NUMBER_OF_BLOCKS
335 )),
336 )
337 }
338 }
339 _ => (false, None),
340 }
341 }
342}
343
344fn read_args_from_stdin() -> SadariEnvironment {
345 let mut sadari_env = SadariEnvironment::default();
346 let mut state = interaction::State::Idle;
347
348 loop {
349 interaction::handle_state_guide(&state, &sadari_env);
350
351 let mut action = String::new();
352 io::stdin().read_line(&mut action).expect("read error");
353
354 let (next_sadari_env, next_state) =
357 interaction::handle_user_input(action, state, sadari_env);
358 state = next_state;
359 sadari_env = next_sadari_env;
360
361 match state {
362 interaction::State::Quit => {
363 process::exit(0);
364 }
365 interaction::State::Done => {
366 break;
367 }
368 _ => {}
369 }
370 }
371
372 sadari_env
373}
374
375fn get_input_from_file(filename: &String) -> Result<Vec<Vec<String>>, io::Error> {
376 let file = File::open(filename)?;
377 let reader = std::io::BufReader::new(&file);
378
379 let mut vec: Vec<Vec<String>> = Vec::new();
380 let mut line_iter = reader.lines();
381
382 (0..NUMBER_OF_LINES_TO_READ).into_iter().for_each(|_| {
383 let line = line_iter.next();
384
385 match line {
386 Some(l) => {
387 let s: String = l.unwrap();
388 let v: Vec<String> = s.split(",").map(move |x| String::from(x.trim())).collect();
389 vec.push(v);
390 }
391 None => {}
392 };
393 });
394
395 Ok(vec)
396}
397
398fn read_args_from_file(args: Vec<String>) -> SadariEnvironment {
399 let filename = &args[1];
400 let vec_read_file = get_input_from_file(filename).unwrap_or_else(|err| {
401 panic!("\n\tget_input_from_file error : {}", err);
402 });
403
404 if vec_read_file.len() < 1 {
405 panic!("\n\ttest input file has few lines, provide 2 lines!");
406 }
407
408 let name_vec: &Vec<String> = vec_read_file
409 .get(0)
410 .ok_or_else(|| "no input for names")
411 .unwrap_or_else(|err| {
412 panic!("\n\tname_vec, test error : {}", err);
413 });
414
415 let number_of_bloks = name_vec.len();
416 if number_of_bloks > MAX_NUMBER_OF_BLOCKS as usize {
417 panic!(
418 "\n\tname_vec length is larger than limit, length: {}, limit {}",
419 number_of_bloks, MAX_NUMBER_OF_BLOCKS
420 );
421 }
422 if number_of_bloks < MIN_NUMBER_OF_BLOCKS as usize {
423 panic!(
424 "\n\tname_vec length is smaller than limit, length: {}, limit {}",
425 number_of_bloks, MIN_NUMBER_OF_BLOCKS
426 );
427 }
428
429 let name_vec = name_vec.clone();
430 let result_vec: Vec<String> = if vec_read_file.len() == 1 {
431 eprintln!("because i got one line, result will be automatically set as number, 0..n");
432 let vec: Vec<String> = (0..number_of_bloks as u8)
433 .into_iter()
434 .map(|x| x.to_string())
435 .collect();
436
437 vec
438 } else {
439 vec_read_file.get(1).unwrap().clone()
440 };
441
442 if name_vec.len() != result_vec.len() {
443 panic!(
444 "\n\tname and result length are different name: {}, result: {}",
445 name_vec.len(),
446 result_vec.len()
447 );
448 }
449
450 SadariEnvironment::default()
451 .number_of_blocks(number_of_bloks as u8)
452 .name_vec(name_vec)
453 .result_vec(result_vec)
454}
455
456pub fn read_args<T>(args: T) -> SadariEnvironment
457where
458 T: Iterator<Item = String>,
459{
460 let args: Vec<String> = args.collect();
461
462 if args.len() >= 2 && ["help", "--help"].contains(&args[1].as_str()) {
463 interaction::help_guide();
464 process::exit(0);
465 }
466
467 if args.len() < 2 {
468 read_args_from_stdin()
469 } else {
470 read_args_from_file(args)
471 }
472}
473
474fn _print_hashmap<K, V>(name: String, hashmap: &HashMap<K, V>)
475where
476 K: Debug + Display,
477 V: Debug,
478{
479 eprintln!("\n{} --------------", &name);
480 for (key, value) in hashmap {
481 eprintln!("key : {}, value : {:?}", key, value);
482 }
483 eprintln!("{} --------------\n", &name);
484}