summaryrefslogtreecommitdiff
path: root/libimaginteraction/src/readline.rs
blob: 7b998dd0c6cd70697a65e5694899132ee8831f4c (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
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
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use error::InteractionErrorKind as IEK;
use error::MapErrInto;

use toml::Value;

use rustyline::{Config, Editor};

pub struct Readline {
    editor: Editor,
    history_file: PathBuf,
    prompt: String,
}

impl Readline {

    pub fn new(rt: &Runtime) -> Result<Readline> {
        let cfg = try!(rt.config().ok_or(IEK::NoConfigError));

        let c = cfg.config();
        let histfile     = try!(c.lookup("ui.cli.readline_history_file").ok_or(IEK::ConfigError));
        let histsize     = try!(c.lookup("ui.cli.readline_history_size").ok_or(IEK::ConfigError));
        let histigndups  = try!(c.lookup("ui.cli.readline_history_ignore_dups").ok_or(IEK::ConfigError));
        let histignspace = try!(c.lookup("ui.cli.readline_history_ignore_space").ok_or(IEK::ConfigError));
        let prompt       = try!(c.lookup("ui.cli.readline_prompt").ok_or(IEK::ConfigError));

        let histfile = try!(match histfile {
            Value::String(s) => PathBuf::from(s),
            _ => Err(IEK::ConfigTypeError.into_error())
                .map_err_into(IEK::ConfigError)
                .map_err_into(IEK::ReadlineError)
        });

        let histsize = try!(match histsize {
            Value::Integer(i) => i,
            _ => Err(IEK::ConfigTypeError.into_error())
                .map_err_into(IEK::ConfigError)
                .map_err_into(IEK::ReadlineError)
        });

        let histigndups = try!(match histigndups {
            Value::Boolean(b) => b,
            _ => Err(IEK::ConfigTypeError.into_error())
                .map_err_into(IEK::ConfigError)
                .map_err_into(IEK::ReadlineError)
        });

        let histignspace = try!(match histignspace {
            Value::Boolean(b) => b,
            _ => Err(IEK::ConfigTypeError.into_error())
                .map_err_into(IEK::ConfigError)
                .map_err_into(IEK::ReadlineError)
        });

        let prompt = try!(match prompt {
            Value::String(s) => s,
            _ => Err(IEK::ConfigTypeError.into_error())
                .map_err_into(IEK::ConfigError)
                .map_err_into(IEK::ReadlineError)
        });

        let config = Config::builder().
            .max_history_size(histsize)
            .history_ignore_dups(histigndups)
            .history_ignore_space(histignspace)
            .build();

        let mut editor = Editor::new(config);

        if !histfile.exists() {
            let _ = try!(File::create(histfile.clone())
                         .map_err_into(IEK::ReadlineHistoryFileCreationError));
        }

        let _ = try!(editor.load_history(&histfile).map_err_into(ReadlineError));

        Ok(Readline {
            editor: editor,
            history_file: histfile,
            prompt: prompt,
        })
    }

    pub fn read_line(&mut self) -> Result<Option<String>> {
        use rustyline::ReadlineError;
        use libimagutil::warn_result::*;

        match self.editor.readline(&self.prompt) {
            Ok(line) => {
                self.editor.add_history_line(&line);
                self.editor
                    .save_history(&self.history_file)
                    .map_warn_err_str(|e| format!("Could not save history file {} -> {:?}",
                                                  self.history_file.display(), e));
                return line;
            },
            Err(ReadlineError::Interrupted) => {
                info!("CTRL-C");
                Ok(None)
            },
            Err(ReadlineError::Eof) => {
                info!("CTRL-D");
                Ok(None)
            },
            Err(err) => Err(err).map_err_into(ReadlineError),

        }
    }

}