1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
//! A simple library for console-based user input, option selection and more.

use console::{style, Key, Term};
use std::{
    io::{self, Write},
    thread,
    time::Duration,
};

/// Reads user input from the console.
///
/// This function prompts the user with a message (`before`) and reads a line of input from the
/// console. The input can be empty unless `allow_empty` is set to `false`. If `new_line` is set
/// to `true`, a newline character will be printed after the prompt.
///
/// # Arguments
///
/// * `before` - The text to display before prompting for input. Add here `\n` for a new line.
/// * `allow_empty` - If true, allows the input to be empty.
///
/// # Returns
///
/// Returns an `Option<T>` containing the user's input converted to the specified type,
/// or `None` if the input is empty and `allow_empty` is `true`.
///
/// # Example
///
/// ```no_run
/// use console_utils::input;
///     
/// let user_input = input::<String>("Enter something: ", false);
///
/// match user_input {
///     Some(value) => println!("You entered: {}", value),
///     None => panic!("The Input cannot be None, allow_empty is false."),
/// }
/// ```
pub fn input<T>(before: &str, allow_empty: bool) -> Option<T>
where
    T: std::str::FromStr,
    T::Err: std::fmt::Debug,
{
    loop {
        print!("{before}");
        flush();

        let mut cli = String::new();
        io::stdin().read_line(&mut cli).unwrap();

        if allow_empty && cli.trim().is_empty() {
            return None;
        } else if !cli.trim().is_empty() {
            match cli.trim().parse() {
                Ok(value) => return Some(value),
                Err(_) => println!("\nWrong Input Type\n"),
            }
        } else {
            println!("\nWrong Input\n");
        }
    }
}

/// Allows the user to select options from a list using the console.
///
/// This function displays a list of options with checkboxes. The user can navigate through the
/// options using arrow keys or 'w' and 's' keys. Pressing the spacebar toggles the selection of
/// the current option. If the user presses Enter, the function returns a vector of booleans
/// indicating which options were selected.
///
/// # Arguments
///
/// * `before` - The text to display before the list of options.
/// * `options` - A vector of strings representing the available options.
/// * `allow_empty` - If true, allows the user to exit without selecting any option.
/// * `multiple` - If true, allows the user to select multiple options.
///
/// # Returns
///
/// Returns an `Option<Vec<bool>>` containing a vector of booleans indicating which options were
/// selected. Returns `None` if no option was selected and `allow_empty` is `true`.
///
/// # Example
///
/// ```no_run
/// use console_utils::select;
///
/// let options = vec![
///     "Option 1",
///     "Option 2",
///     "Option 3",
/// ];
///
/// let selected_indices = select("Select an option:", &options, false, false);
///
/// match selected_indices {
///     Some(indices) => {
///         println!("Selected indices: {:?}", indices);
///     }
///     None => {
///         println!("No option selected.");
///     }
/// }
/// ```
pub fn select(
    before: &str,
    options: &[&str],
    allow_empty: bool,
    multiple: bool,
) -> Option<Vec<bool>> {
    loop {
        let mut matrix: Vec<bool> = vec![];
        let mut i = 0;
        let stdout = Term::buffered_stdout();

        // print everything
        println!("{}", before,);

        for i in options {
            println!("[ ] {}", i);
            matrix.push(false);
        }

        // move the cursor to the first item
        move_cursor_up(options.len());
        move_cursor_right(options[i].len() + 4);

        // input reaction loop
        loop {
            if let Ok(character) = stdout.read_key() {
                match character {
                    Key::ArrowUp | Key::Char('w') => {
                        if i > 0 {
                            move_cursor_up(1);
                            move_cursor_left(options[i].len() + 4);
                            i -= 1;
                            move_cursor_right(options[i].len() + 4);
                        }
                    }
                    Key::ArrowDown | Key::Char('s') => {
                        if i < options.len() - 1 {
                            move_cursor_down(1);
                            move_cursor_left(options[i].len() + 4);
                            i += 1;
                            move_cursor_right(options[i].len() + 4);
                        }
                    }
                    Key::Char(' ') => {
                        clear_line();
                        if matrix[i] {
                            print!("[ ] {}", options[i]);
                            matrix[i] = false;
                        } else {
                            print!("[{}] {}", style("*").cyan(), options[i]);
                            matrix[i] = true;
                        }
                        flush();
                    }
                    Key::Enter => {
                        break;
                    }
                    _ => {}
                }
            }
        }

        // process input
        if matrix.iter().filter(|&&selected| selected).count() > 1 && !multiple {
            reset("\nPlease Select only one!\n", options.len());
        } else if allow_empty && matrix.iter().all(|&x| !x) {
            reset("", options.len());
            return None;
        } else if !matrix.iter().all(|&x| !x) {
            reset("", options.len());
            return Some(matrix);
        } else {
            reset("\nPlease Select any option!\n", options.len());
        }
    }
}

// Internal function for resetting the console.
fn reset(mes: &str, len: usize) {
    move_cursor_down(len);
    println!("{mes}");
}

/// Enumeration representing different types of spinners.
#[derive(Debug, Clone)]
pub enum SpinnerType {
    Standard,
    Dots,
    Box,
    Flip,
    Custom(Vec<&'static str>),
}

impl SpinnerType {
    /// Converts the spinner type to a vector of frames, gives back the following variants:
    ///  - `SpinnerType::Standard`: Standard spinner with characters / - \ |.
    ///  - `SpinnerType::Dots`: Spinner with dots . .. ... .....
    ///  - `SpinnerType::Box`: Spinner with box characters ▌ ▀ ▐ ▄.
    ///  - `SpinnerType::Flip`: Spinner with flip characters _ _ _ - \ ' ´ - _ _ _.
    ///  - `SpinnerType::Custom(frames)`: Custom spinner with user-defined frames.
    pub fn to_frames(&self) -> Vec<&'static str> {
        match self {
            SpinnerType::Standard => vec!["/", "-", "\\", "|"],
            SpinnerType::Dots => vec![".", "..", "...", "....", "...", ".."],
            SpinnerType::Box => vec!["▌", "▀", "▐", "▄"],
            SpinnerType::Flip => vec!["_", "_", "_", "-", "`", "`", "'", "´", "-", "_", "_", "_"],
            SpinnerType::Custom(frames) => frames.to_owned(),
        }
    }
}

/// Displays a console-based spinner animation.
///
/// A spinner is a visual indicator of a long-running process. It consists of a set of frames
/// that are displayed sequentially to create the appearance of motion.
///
/// # Parameters
///
/// - `time`: A floating-point number representing the duration of the spinner animation in seconds.
/// - `spinner_type`: The type of spinner to display.
///
/// # Example
///
/// ```rust
/// use console_utils::{spinner, SpinnerType};
///
/// // Display a standard spinner for 3 seconds
/// spinner(3.0, SpinnerType::Standard);
///
/// // Display a custom spinner for 2 seconds
/// spinner(2.0, SpinnerType::Custom(vec!["1", "2", "3", "4", "3", "2"]));
/// ```
pub fn spinner(mut time: f64, spinner_type: SpinnerType) {
    let frames = spinner_type.to_frames();
    let mut i = 0;

    while time > 0.0 {
        clear_line();
        print!("{}", frames[i]);
        flush();
        thread::sleep(Duration::from_secs_f64(0.075));
        time -= 0.075;
        if i < frames.len() - 1 {
            i += 1
        } else {
            i = 0
        }
    }

    clear_line();
}

/// Flushes the output buffer, ensuring that all content is written to the console.
///
/// # Example
///
/// ```rust
/// use console_utils::flush;
///
/// // Flush the output buffer to ensure content is displayed immediately
/// flush();
/// ```
pub fn flush() {
    io::stdout().flush().unwrap();
}

/// Clears the current line in the console.
///
/// This function uses ANSI escape codes to clear the entire line and move the cursor to the
/// beginning of the line.
///
/// # Example
///
/// ```rust
/// use console_utils::clear_line;
///
/// // Clear the current line
/// clear_line();
/// ```
pub fn clear_line() {
    print!("\r\x1b[2K");
    flush();
}

/// Moves the cursor down by the specified number of lines.
///
/// # Arguments
///
/// * `n` - The number of lines to move the cursor down.
///
/// # Example
///
/// ```rust
/// use console_utils::move_cursor_down;
///
/// // Move the cursor down by 2 lines
/// move_cursor_down(2);
/// ```
pub fn move_cursor_down(n: usize) {
    if n > 0 {
        print!("\x1b[{}B", n);
        flush();
    }
}

/// Moves the cursor up by the specified number of lines.
///
/// # Arguments
///
/// * `n` - The number of lines to move the cursor up.
///
/// # Example
///
/// ```rust
/// use console_utils::move_cursor_up;
///
/// // Move the cursor up by 3 lines
/// move_cursor_up(3);
/// ```
pub fn move_cursor_up(n: usize) {
    if n > 0 {
        print!("\x1b[{}A", n);
        flush();
    }
}

/// Moves the cursor to the left by the specified number of characters.
///
/// # Arguments
///
/// * `n` - The number of characters to move the cursor to the left.
///
/// # Example
///
/// ```rust
/// use console_utils::move_cursor_left;
///
/// // Move the cursor left by 4 characters
/// move_cursor_left(4);
/// ```
pub fn move_cursor_left(n: usize) {
    if n > 0 {
        print!("\x1b[{}D", n);
        flush();
    }
}

/// Moves the cursor to the right by the specified number of characters.
///
/// # Arguments
///
/// * `n` - The number of characters to move the cursor to the right.
///
/// # Example
///
/// ```rust
/// use console_utils::move_cursor_right;
///
/// // Move the cursor right by 5 characters
/// move_cursor_right(5);
/// ```
pub fn move_cursor_right(n: usize) {
    if n > 0 {
        print!("\x1b[{}C", n);
        flush();
    }
}

/// Moves the cursor to the specified position on the console.
///
/// # Arguments
///
/// * `x` - The horizontal position (column) to move the cursor to.
/// * `y` - The vertical position (row) to move the cursor to.
///
/// # Example
///
/// ```rust
/// use console_utils::move_cursor_to;
///
/// // Move the cursor to column 3, row 5
/// move_cursor_to(3, 5);
/// ```
pub fn move_cursor_to(x: usize, y: usize) {
    print!("\x1B[{};{}H", y + 1, x + 1);
    flush();
}

/// Reveals a string gradually, printing one character at a time with a specified time interval.
///
/// This function is useful for creating a typing effect or slowly displaying information to the user.
///
/// # Arguments
///
/// * `str` - The string to reveal gradually. Add here `\n` for a new line.
/// * `time_between` - The time interval (in seconds) between each revealed character.
///
/// # Example
///
/// ```rust
/// use console_utils::reveal;
///
/// // Display "Hello World!" with a time interval of 0.1 seconds between each character and a new line after it's finished.
/// reveal("Hello World!", 0.1);
/// ```
pub fn reveal(str: &str, time_between: f64) {
    for i in 0..str.len() {
        print!("{}", str.chars().nth(i).unwrap_or(' '));
        flush();
        thread::sleep(Duration::from_secs_f64(time_between));
    }
}