summaryrefslogtreecommitdiff
path: root/src/backends/stdio.rs
blob: 627714a721539aedb0d0e2dcdd995eb93dd2c8af (plain)
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
// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT

use std::io::{self, Write};

use crate::{Choice, FileSelection, Input, Message, Password, Question, Result};

/// The fallback backend using standard input and output.
///
/// This backend is intended as a fallback backend to use if no other backend is available.  The
/// dialogs are printed to the standard output and user input is read from the standard input.
#[derive(Debug, Default)]
pub struct Stdio {}

impl Stdio {
    /// Creates a new `Stdio` instance.
    pub fn new() -> Stdio {
        Default::default()
    }
}

impl AsRef<Stdio> for Stdio {
    fn as_ref(&self) -> &Self {
        self
    }
}

fn print_title(title: &Option<String>) {
    if let Some(ref title) = title {
        println!("{}", title);
        println!("{}", "=".repeat(title.len()));
    }
}

fn read_input() -> Result<String> {
    let mut input = String::new();
    io::stdin().read_line(&mut input)?;
    Ok(input.trim_end_matches('\n').to_string())
}

fn parse_choice(input: &str) -> Choice {
    match input.to_lowercase().as_ref() {
        "y" => Choice::Yes,
        "yes" => Choice::Yes,
        "n" => Choice::No,
        "no" => Choice::No,
        _ => Choice::Cancel,
    }
}

impl super::Backend for Stdio {
    fn show_input(&self, input: &Input) -> Result<Option<String>> {
        print_title(&input.title);
        if let Some(ref default) = input.default {
            print!("{} [default: {}]: ", input.text, default);
        } else {
            print!("{}: ", input.text);
        }
        io::stdout().flush()?;

        let user_input = read_input()?;
        if user_input.is_empty() {
            if let Some(ref default) = input.default {
                return Ok(Some(default.to_string()));
            }
        }
        Ok(Some(user_input))
    }

    fn show_message(&self, message: &Message) -> Result<()> {
        print_title(&message.title);
        println!("{}", message.text);
        Ok(())
    }

    fn show_password(&self, password: &Password) -> Result<Option<String>> {
        print_title(&password.title);
        print!("{}: ", password.text);
        io::stdout().flush()?;
        Ok(Some(rpassword::read_password()?))
    }

    fn show_question(&self, question: &Question) -> Result<Choice> {
        print_title(&question.title);
        print!("{} [y/n]: ", question.text);
        io::stdout().flush()?;
        Ok(parse_choice(&read_input()?))
    }

    fn show_file_selection(&self, file_selection: &FileSelection) -> Result<Option<String>> {
        let dir = file_selection.path_to_string().ok_or("path not valid")?;
        print_title(&file_selection.title);
        print!("{} [{}]: ", file_selection.text, dir);
        io::stdout().flush()?;
        let result = read_input()?;
        if result.starts_with('/') {
            Ok(Some(result))
        } else {
            Ok(Some(dir + &result))
        }
    }
}