1#![allow(unknown_lints)]
19#![allow(bare_trait_objects, ellipsis_inclusive_range_patterns)]
22
23#![allow(deprecated)]
25
26extern crate libc;
27#[macro_use]
28extern crate log;
29extern crate memchr;
30#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
31extern crate nix;
32extern crate unicode_segmentation;
33extern crate unicode_width;
34#[cfg(unix)]
35extern crate utf8parse;
36#[cfg(windows)]
37extern crate winapi;
38#[cfg(windows)]
39extern crate kernel32;
40
41pub mod completion;
42pub mod config;
43mod edit;
44pub mod error;
45pub mod highlight;
46pub mod hint;
47pub mod history;
48mod keymap;
49mod keys;
50mod kill_ring;
51pub mod line_buffer;
52mod undo;
53
54mod tty;
55
56use std::collections::HashMap;
57use std::fmt;
58use std::io::{self, Write};
59use std::path::Path;
60use std::result;
61use std::sync::{Arc, Mutex, RwLock};
62use unicode_width::UnicodeWidthStr;
63
64use tty::{RawMode, RawReader, Renderer, Term, Terminal};
65
66use completion::{longest_common_prefix, Candidate, Completer};
67pub use config::{ColorMode, CompletionType, Config, EditMode, HistoryDuplicates};
68use edit::State;
69use highlight::Highlighter;
70use hint::Hinter;
71use history::{Direction, History};
72pub use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
73use keymap::{InputState, Refresher};
74pub use keys::KeyPress;
75use kill_ring::{KillRing, Mode};
76use line_buffer::WordAction;
77
78pub type Result<T> = result::Result<T, error::ReadlineError>;
80
81fn complete_line<R: RawReader, C: Completer>(
83 rdr: &mut R,
84 s: &mut State,
85 input_state: &mut InputState,
86 completer: &C,
87 highlighter: Option<&dyn Highlighter>,
88 config: &Config,
89) -> Result<Option<Cmd>> {
90 let (start, candidates) = try!(completer.complete(&s.line, s.line.pos()));
92 if candidates.is_empty() {
94 try!(s.out.beep());
95 Ok(None)
96 } else if CompletionType::Circular == config.completion_type() {
97 let mark = s.changes.borrow_mut().begin();
98 let backup = s.line.as_str().to_owned();
100 let backup_pos = s.line.pos();
101 let mut cmd;
102 let mut i = 0;
103 loop {
104 if i < candidates.len() {
106 let candidate = candidates[i].replacement();
107 completer.update(&mut s.line, start, candidate);
114 try!(s.refresh_line());
115 } else {
116 s.line.update(&backup, backup_pos);
118 try!(s.refresh_line());
119 }
120
121 cmd = try!(s.next_cmd(input_state, rdr, true));
122 match cmd {
123 Cmd::Complete => {
124 i = (i + 1) % (candidates.len() + 1); if i == candidates.len() {
126 try!(s.out.beep());
127 }
128 }
129 Cmd::Abort => {
130 if i < candidates.len() {
132 s.line.update(&backup, backup_pos);
133 try!(s.refresh_line());
134 }
135 s.changes.borrow_mut().truncate(mark);
136 return Ok(None);
137 }
138 _ => {
139 s.changes.borrow_mut().end();
140 break;
141 }
142 }
143 }
144 Ok(Some(cmd))
145 } else if CompletionType::List == config.completion_type() {
146 if let Some(lcp) = longest_common_prefix(&candidates) {
147 if lcp.len() > s.line.pos() - start {
149 completer.update(&mut s.line, start, lcp);
150 try!(s.refresh_line());
151 }
152 }
153 if candidates.len() > 1 {
155 try!(s.out.beep());
156 } else {
157 return Ok(None);
158 }
159 let mut cmd = try!(s.next_cmd(input_state, rdr, true));
161 if cmd != Cmd::Complete {
163 return Ok(Some(cmd));
164 }
165 let save_pos = s.line.pos();
167 try!(s.edit_move_end());
168 s.line.set_pos(save_pos);
169 let show_completions = if candidates.len() > config.completion_prompt_limit() {
171 let msg = format!("\nDisplay all {} possibilities? (y or n)", candidates.len());
172 try!(s.out.write_and_flush(msg.as_bytes()));
173 s.old_rows += 1;
174 while cmd != Cmd::SelfInsert(1, 'y')
175 && cmd != Cmd::SelfInsert(1, 'Y')
176 && cmd != Cmd::SelfInsert(1, 'n')
177 && cmd != Cmd::SelfInsert(1, 'N')
178 && cmd != Cmd::Kill(Movement::BackwardChar(1))
179 {
180 cmd = try!(s.next_cmd(input_state, rdr, false));
181 }
182 match cmd {
183 Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') => true,
184 _ => false,
185 }
186 } else {
187 true
188 };
189 if show_completions {
190 page_completions(rdr, s, input_state, highlighter, &candidates)
191 } else {
192 try!(s.refresh_line());
193 Ok(None)
194 }
195 } else {
196 Ok(None)
197 }
198}
199
200fn page_completions<R: RawReader, C: Candidate>(
201 rdr: &mut R,
202 s: &mut State,
203 input_state: &mut InputState,
204 highlighter: Option<&dyn Highlighter>,
205 candidates: &[C],
206) -> Result<Option<Cmd>> {
207 use std::cmp;
208
209 let min_col_pad = 2;
210 let cols = s.out.get_columns();
211 let max_width = cmp::min(
212 cols,
213 candidates
214 .into_iter()
215 .map(|s| s.display().width())
216 .max()
217 .unwrap()
218 + min_col_pad,
219 );
220 let num_cols = cols / max_width;
221
222 let mut pause_row = s.out.get_rows() - 1;
223 let num_rows = (candidates.len() + num_cols - 1) / num_cols;
224 let mut ab = String::new();
225 for row in 0..num_rows {
226 if row == pause_row {
227 try!(s.out.write_and_flush(b"\n--More--"));
228 let mut cmd = Cmd::Noop;
229 while cmd != Cmd::SelfInsert(1, 'y')
230 && cmd != Cmd::SelfInsert(1, 'Y')
231 && cmd != Cmd::SelfInsert(1, 'n')
232 && cmd != Cmd::SelfInsert(1, 'N')
233 && cmd != Cmd::SelfInsert(1, 'q')
234 && cmd != Cmd::SelfInsert(1, 'Q')
235 && cmd != Cmd::SelfInsert(1, ' ')
236 && cmd != Cmd::Kill(Movement::BackwardChar(1))
237 && cmd != Cmd::AcceptLine
238 {
239 cmd = try!(s.next_cmd(input_state, rdr, false));
240 }
241 match cmd {
242 Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') | Cmd::SelfInsert(1, ' ') => {
243 pause_row += s.out.get_rows() - 1;
244 }
245 Cmd::AcceptLine => {
246 pause_row += 1;
247 }
248 _ => break,
249 }
250 try!(s.out.write_and_flush(b"\n"));
251 } else {
252 try!(s.out.write_and_flush(b"\n"));
253 }
254 ab.clear();
255 for col in 0..num_cols {
256 let i = (col * num_rows) + row;
257 if i < candidates.len() {
258 let candidate = &candidates[i].display();
259 let width = candidate.width();
260 if let Some(highlighter) = highlighter {
261 ab.push_str(&highlighter.highlight_candidate(candidate, CompletionType::List));
262 } else {
263 ab.push_str(candidate);
264 }
265 if ((col + 1) * num_rows) + row < candidates.len() {
266 for _ in width..max_width {
267 ab.push(' ');
268 }
269 }
270 }
271 }
272 try!(s.out.write_and_flush(ab.as_bytes()));
273 }
274 try!(s.out.write_and_flush(b"\n"));
275 try!(s.refresh_line());
276 Ok(None)
277}
278
279fn reverse_incremental_search<R: RawReader>(
281 rdr: &mut R,
282 s: &mut State,
283 input_state: &mut InputState,
284 history: &History,
285) -> Result<Option<Cmd>> {
286 if history.is_empty() {
287 return Ok(None);
288 }
289 let mark = s.changes.borrow_mut().begin();
290 let backup = s.line.as_str().to_owned();
292 let backup_pos = s.line.pos();
293
294 let mut search_buf = String::new();
295 let mut history_idx = history.len() - 1;
296 let mut direction = Direction::Reverse;
297 let mut success = true;
298
299 let mut cmd;
300 loop {
302 let prompt = if success {
303 format!("(reverse-i-search)`{}': ", search_buf)
304 } else {
305 format!("(failed reverse-i-search)`{}': ", search_buf)
306 };
307 try!(s.refresh_prompt_and_line(&prompt));
308
309 cmd = try!(s.next_cmd(input_state, rdr, true));
310 if let Cmd::SelfInsert(_, c) = cmd {
311 search_buf.push(c);
312 } else {
313 match cmd {
314 Cmd::Kill(Movement::BackwardChar(_)) => {
315 search_buf.pop();
316 continue;
317 }
318 Cmd::ReverseSearchHistory => {
319 direction = Direction::Reverse;
320 if history_idx > 0 {
321 history_idx -= 1;
322 } else {
323 success = false;
324 continue;
325 }
326 }
327 Cmd::ForwardSearchHistory => {
328 direction = Direction::Forward;
329 if history_idx < history.len() - 1 {
330 history_idx += 1;
331 } else {
332 success = false;
333 continue;
334 }
335 }
336 Cmd::Abort => {
337 s.line.update(&backup, backup_pos);
339 try!(s.refresh_line());
340 s.changes.borrow_mut().truncate(mark);
341 return Ok(None);
342 }
343 Cmd::Move(_) => {
344 try!(s.refresh_line()); break;
346 }
347 _ => break,
348 }
349 }
350 success = match history.search(&search_buf, history_idx, direction) {
351 Some(idx) => {
352 history_idx = idx;
353 let entry = history.get(idx).unwrap();
354 let pos = entry.find(&search_buf).unwrap();
355 s.line.update(entry, pos);
356 true
357 }
358 _ => false,
359 };
360 }
361 s.changes.borrow_mut().end();
362 Ok(Some(cmd))
363}
364
365fn readline_edit<H: Helper>(
369 prompt: &str,
370 initial: Option<(&str, &str)>,
371 editor: &mut Editor<H>,
372 original_mode: &tty::Mode,
373) -> Result<String> {
374 let completer = editor.helper.as_ref();
375 let hinter = editor.helper.as_ref().map(|h| h as &dyn Hinter);
376 let highlighter = if editor.term.colors_enabled() {
377 editor.helper.as_ref().map(|h| h as &dyn Highlighter)
378 } else {
379 None
380 };
381
382 let mut stdout = editor.term.create_writer();
383
384 editor.reset_kill_ring(); let mut s = State::new(
386 &mut stdout,
387 prompt,
388 editor.history.len(),
389 hinter,
390 highlighter,
391 );
392 let mut input_state = InputState::new(&editor.config, Arc::clone(&editor.custom_bindings));
393
394 s.line.set_delete_listener(editor.kill_ring.clone());
395 s.line.set_change_listener(s.changes.clone());
396
397 if let Some((left, right)) = initial {
398 s.line
399 .update((left.to_owned() + right).as_ref(), left.len());
400 }
401
402 try!(s.refresh_line());
403
404 let mut rdr = try!(editor.term.create_reader(&editor.config));
405
406 loop {
407 let rc = s.next_cmd(&mut input_state, &mut rdr, false);
408 let mut cmd = try!(rc);
409
410 if cmd.should_reset_kill_ring() {
411 editor.reset_kill_ring();
412 }
413
414 if cmd == Cmd::Complete && completer.is_some() {
416 let next = try!(complete_line(
417 &mut rdr,
418 &mut s,
419 &mut input_state,
420 completer.unwrap(),
421 highlighter,
422 &editor.config,
423 ));
424 if next.is_some() {
425 cmd = next.unwrap();
426 } else {
427 continue;
428 }
429 }
430
431 if let Cmd::SelfInsert(n, c) = cmd {
432 try!(s.edit_insert(c, n));
433 continue;
434 } else if let Cmd::Insert(n, text) = cmd {
435 try!(s.edit_yank(&input_state, &text, Anchor::Before, n));
436 continue;
437 }
438
439 if cmd == Cmd::ReverseSearchHistory {
440 let next = try!(reverse_incremental_search(
442 &mut rdr,
443 &mut s,
444 &mut input_state,
445 &editor.history,
446 ));
447 if next.is_some() {
448 cmd = next.unwrap();
449 } else {
450 continue;
451 }
452 }
453
454 match cmd {
455 Cmd::Move(Movement::BeginningOfLine) => {
456 try!(s.edit_move_home())
458 }
459 Cmd::Move(Movement::ViFirstPrint) => {
460 try!(s.edit_move_home());
461 try!(s.edit_move_to_next_word(At::Start, Word::Big, 1))
462 }
463 Cmd::Move(Movement::BackwardChar(n)) => {
464 try!(s.edit_move_backward(n))
466 }
467 Cmd::ReplaceChar(n, c) => try!(s.edit_replace_char(c, n)),
468 Cmd::Replace(mvt, text) => {
469 try!(s.edit_kill(&mvt));
470 if let Some(text) = text {
471 try!(s.edit_insert_text(&text))
472 }
473 }
474 Cmd::Overwrite(c) => {
475 try!(s.edit_overwrite_char(c));
476 }
477 Cmd::EndOfFile => {
478 if !input_state.is_emacs_mode() && !s.line.is_empty() {
479 try!(s.edit_move_end());
480 break;
481 } else if s.line.is_empty() {
482 return Err(error::ReadlineError::Eof);
483 } else {
484 try!(s.edit_delete(1))
485 }
486 }
487 Cmd::Move(Movement::EndOfLine) => {
488 try!(s.edit_move_end())
490 }
491 Cmd::Move(Movement::ForwardChar(n)) => {
492 try!(s.edit_move_forward(n))
494 }
495 Cmd::ClearScreen => {
496 try!(s.out.clear_screen());
498 try!(s.refresh_line())
499 }
500 Cmd::NextHistory => {
501 try!(s.edit_history_next(&editor.history, false))
503 }
504 Cmd::PreviousHistory => {
505 try!(s.edit_history_next(&editor.history, true))
507 }
508 Cmd::HistorySearchBackward => {
509 try!(s.edit_history_search(&editor.history, Direction::Reverse))
510 }
511 Cmd::HistorySearchForward => {
512 try!(s.edit_history_search(&editor.history, Direction::Forward))
513 }
514 Cmd::TransposeChars => {
515 try!(s.edit_transpose_chars())
517 }
518 #[cfg(unix)]
519 Cmd::QuotedInsert => {
520 let c = try!(rdr.next_char());
522 try!(s.edit_insert(c, 1)) }
524 Cmd::Yank(n, anchor) => {
525 let mut kill_ring = editor.kill_ring.lock().unwrap();
527 if let Some(text) = kill_ring.yank() {
528 try!(s.edit_yank(&input_state, text, anchor, n))
529 }
530 }
531 Cmd::ViYankTo(ref mvt) => {
532 if let Some(text) = s.line.copy(mvt) {
533 let mut kill_ring = editor.kill_ring.lock().unwrap();
534 kill_ring.kill(&text, Mode::Append)
535 }
536 }
537 Cmd::AcceptLine => {
539 #[cfg(test)]
540 {
541 editor.term.cursor = s.cursor.col;
542 }
543 try!(s.edit_move_end());
545 if s.hinter.is_some() {
546 s.hinter = None;
549 try!(s.refresh_line());
550 }
551 break;
552 }
553 Cmd::BeginningOfHistory => {
554 try!(s.edit_history(&editor.history, true))
556 }
557 Cmd::EndOfHistory => {
558 try!(s.edit_history(&editor.history, false))
560 }
561 Cmd::Move(Movement::BackwardWord(n, word_def)) => {
562 try!(s.edit_move_to_prev_word(word_def, n))
564 }
565 Cmd::CapitalizeWord => {
566 try!(s.edit_word(WordAction::CAPITALIZE))
568 }
569 Cmd::Kill(ref mvt) => {
570 try!(s.edit_kill(mvt));
571 }
572 Cmd::Move(Movement::ForwardWord(n, at, word_def)) => {
573 try!(s.edit_move_to_next_word(at, word_def, n))
575 }
576 Cmd::DowncaseWord => {
577 try!(s.edit_word(WordAction::LOWERCASE))
579 }
580 Cmd::TransposeWords(n) => {
581 try!(s.edit_transpose_words(n))
583 }
584 Cmd::UpcaseWord => {
585 try!(s.edit_word(WordAction::UPPERCASE))
587 }
588 Cmd::YankPop => {
589 let mut kill_ring = editor.kill_ring.lock().unwrap();
591 if let Some((yank_size, text)) = kill_ring.yank_pop() {
592 try!(s.edit_yank_pop(yank_size, text))
593 }
594 }
595 Cmd::Move(Movement::ViCharSearch(n, cs)) => try!(s.edit_move_to(cs, n)),
596 Cmd::Undo(n) => {
597 s.line.remove_change_listener();
598 if s.changes.borrow_mut().undo(&mut s.line, n) {
599 try!(s.refresh_line());
600 }
601 s.line.set_change_listener(s.changes.clone());
602 }
603 Cmd::Interrupt => {
604 return Err(error::ReadlineError::Interrupted);
605 }
606 #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
607 Cmd::Suspend => {
608 try!(original_mode.disable_raw_mode());
609 try!(tty::suspend());
610 try!(editor.term.enable_raw_mode()); try!(s.refresh_line());
612 continue;
613 }
614 Cmd::Noop => {}
615 _ => {
616 }
618 }
619 }
620 if cfg!(windows) {
621 let _ = original_mode; }
623 Ok(s.line.into_string())
624}
625
626struct Guard<'m>(&'m tty::Mode);
627
628#[allow(unused_must_use)]
629impl<'m> Drop for Guard<'m> {
630 fn drop(&mut self) {
631 let Guard(mode) = *self;
632 mode.disable_raw_mode();
633 }
634}
635
636fn readline_raw<H: Helper>(
639 prompt: &str,
640 initial: Option<(&str, &str)>,
641 editor: &mut Editor<H>,
642) -> Result<String> {
643 let original_mode = try!(editor.term.enable_raw_mode());
644 let guard = Guard(&original_mode);
645 let user_input = readline_edit(prompt, initial, editor, &original_mode);
646 if editor.config.auto_add_history() {
647 if let Ok(ref line) = user_input {
648 editor.add_history_entry(&*line);
649 }
650 }
651 drop(guard); editor
653 .term
654 .create_writer()
655 .write_and_flush(b"\n")
656 .unwrap();
657 user_input
658}
659
660fn readline_direct() -> Result<String> {
661 let mut line = String::new();
662 if try!(io::stdin().read_line(&mut line)) > 0 {
663 Ok(line)
664 } else {
665 Err(error::ReadlineError::Eof)
666 }
667}
668
669pub trait Helper
674where
675 Self: Completer,
676 Self: Hinter,
677 Self: Highlighter,
678{
679}
680
681impl Helper for () {}
682
683pub struct Editor<H: Helper> {
685 term: Terminal,
686 history: History,
687 helper: Option<H>,
688 kill_ring: Arc<Mutex<KillRing>>,
689 config: Config,
690 custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
691}
692
693impl<H: Helper> Editor<H> {
695 pub fn new() -> Editor<H> {
697 Self::with_config(Config::default())
698 }
699
700 pub fn with_config(config: Config) -> Editor<H> {
702 let term = Terminal::new(config.color_mode(), config.output_stream());
703 Editor {
704 term,
705 history: History::with_config(config),
706 helper: None,
707 kill_ring: Arc::new(Mutex::new(KillRing::new(60))),
708 config,
709 custom_bindings: Arc::new(RwLock::new(HashMap::new())),
710 }
711 }
712
713 pub fn readline(&mut self, prompt: &str) -> Result<String> {
720 self.readline_with(prompt, None)
721 }
722
723 pub fn readline_with_initial(&mut self, prompt: &str, initial: (&str, &str)) -> Result<String> {
731 self.readline_with(prompt, Some(initial))
732 }
733
734 fn readline_with(&mut self, prompt: &str, initial: Option<(&str, &str)>) -> Result<String> {
735 if self.term.is_unsupported() {
736 debug!(target: "rustyline", "unsupported terminal");
737 let mut stdout = io::stdout();
739 try!(stdout.write_all(prompt.as_bytes()));
740 try!(stdout.flush());
741
742 readline_direct()
743 } else if !self.term.is_stdin_tty() {
744 debug!(target: "rustyline", "stdin is not a tty");
745 readline_direct()
747 } else {
748 readline_raw(prompt, initial, self)
749 }
750 }
751
752 pub fn load_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<()> {
754 self.history.load(path)
755 }
756
757 pub fn save_history<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<()> {
759 self.history.save(path)
760 }
761
762 pub fn add_history_entry<S: AsRef<str> + Into<String>>(&mut self, line: S) -> bool {
764 self.history.add(line)
765 }
766
767 pub fn clear_history(&mut self) {
769 self.history.clear()
770 }
771
772 pub fn history_mut(&mut self) -> &mut History {
774 &mut self.history
775 }
776
777 pub fn history(&self) -> &History {
779 &self.history
780 }
781
782 pub fn set_helper(&mut self, helper: Option<H>) {
785 self.helper = helper;
786 }
787
788 pub fn helper_mut(&mut self) -> Option<&mut H> {
790 self.helper.as_mut()
791 }
792
793 pub fn helper(&self) -> Option<&H> {
795 self.helper.as_ref()
796 }
797
798 pub fn bind_sequence(&mut self, key_seq: KeyPress, cmd: Cmd) -> Option<Cmd> {
800 let mut bindings = self.custom_bindings.write().unwrap();
801 bindings.insert(key_seq, cmd)
802 }
803
804 pub fn unbind_sequence(&mut self, key_seq: KeyPress) -> Option<Cmd> {
806 let mut bindings = self.custom_bindings.write().unwrap();
807 bindings.remove(&key_seq)
808 }
809
810 pub fn iter<'a>(&'a mut self, prompt: &'a str) -> Iter<H> {
825 Iter {
826 editor: self,
827 prompt,
828 }
829 }
830
831 fn reset_kill_ring(&self) {
832 let mut kill_ring = self.kill_ring.lock().unwrap();
833 kill_ring.reset();
834 }
835}
836
837impl<H: Helper> config::Configurer for Editor<H> {
838 fn config_mut(&mut self) -> &mut Config {
839 &mut self.config
840 }
841
842 fn set_max_history_size(&mut self, max_size: usize) {
843 self.config_mut().set_max_history_size(max_size);
844 self.history.set_max_len(max_size);
845 }
846
847 fn set_history_ignore_dups(&mut self, yes: bool) {
848 self.config_mut().set_history_ignore_dups(yes);
849 self.history.ignore_dups = yes;
850 }
851
852 fn set_history_ignore_space(&mut self, yes: bool) {
853 self.config_mut().set_history_ignore_space(yes);
854 self.history.ignore_space = yes;
855 }
856
857 fn set_color_mode(&mut self, color_mode: ColorMode) {
858 self.config_mut().set_color_mode(color_mode);
859 self.term.color_mode = color_mode;
860 }
861}
862
863impl<H: Helper> fmt::Debug for Editor<H> {
864 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
865 f.debug_struct("Editor")
866 .field("term", &self.term)
867 .field("config", &self.config)
868 .finish()
869 }
870}
871
872pub struct Iter<'a, H: Helper>
874where
875 H: 'a,
876{
877 editor: &'a mut Editor<H>,
878 prompt: &'a str,
879}
880
881impl<'a, H: Helper> Iterator for Iter<'a, H> {
882 type Item = Result<String>;
883
884 fn next(&mut self) -> Option<Result<String>> {
885 let readline = self.editor.readline(self.prompt);
886 match readline {
887 Ok(l) => Some(Ok(l)),
888 Err(error::ReadlineError::Eof) => None,
889 e @ Err(_) => Some(e),
890 }
891 }
892}
893
894enum StdStream {
895 Stdout(io::Stdout),
896 Stderr(io::Stderr),
897}
898impl StdStream {
899 fn from_stream_type(t: config::OutputStreamType) -> StdStream {
900 match t {
901 config::OutputStreamType::Stderr => StdStream::Stderr(io::stderr()),
902 config::OutputStreamType::Stdout => StdStream::Stdout(io::stdout()),
903 }
904 }
905}
906#[cfg(unix)]
907impl std::os::unix::io::AsRawFd for StdStream {
908 fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
909 match self {
910 StdStream::Stdout(e) => e.as_raw_fd(),
911 StdStream::Stderr(e) => e.as_raw_fd(),
912 }
913 }
914}
915impl io::Write for StdStream {
916 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
917 match self {
918 StdStream::Stdout(ref mut e) => e.write(buf),
919 StdStream::Stderr(ref mut e) => e.write(buf),
920 }
921 }
922
923 fn flush(&mut self) -> io::Result<()> {
924 match self {
925 StdStream::Stdout(ref mut e) => e.flush(),
926 StdStream::Stderr(ref mut e) => e.flush(),
927 }
928 }
929
930 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
931 match self {
932 StdStream::Stdout(ref mut e) => e.write_all(buf),
933 StdStream::Stderr(ref mut e) => e.write_all(buf),
934 }
935 }
936
937 fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> io::Result<()> {
938 match self {
939 StdStream::Stdout(ref mut e) => e.write_fmt(fmt),
940 StdStream::Stderr(ref mut e) => e.write_fmt(fmt),
941 }
942 }
943
944 fn by_ref(&mut self) -> &mut StdStream {
945 self
946 }
947}
948
949#[cfg(test)]
950#[macro_use]
951extern crate assert_matches;
952#[cfg(test)]
953mod test;