microsoft/openvmm
Publicmirrored from https://github.com/microsoft/openvmmAvailable
hyperv/tools/hypestv/src/windows/rustyline_printer.rs
59lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | //! Implements a `Write` adapter for `rustyline::ExternalPrinter`. This allows |
| 5 | //! writing to the console while there is an active rustyline prompt, without it |
| 6 | //! overwriting the prompt. |
| 7 | //! |
| 8 | //! Ideally `rustyline` would just provide this facility natively. |
| 9 | |
| 10 | use parking_lot::Mutex; |
| 11 | use std::io::LineWriter; |
| 12 | use std::io::Write; |
| 13 | use std::sync::Arc; |
| 14 | |
| 15 | #[derive(Clone)] |
| 16 | pub struct Printer(Arc<Mutex<LineWriter<PrinterInner>>>); |
| 17 | |
| 18 | pub struct PrinterWriter<'a>( |
| 19 | parking_lot::lock_api::MutexGuard<'a, parking_lot::RawMutex, LineWriter<PrinterInner>>, |
| 20 | ); |
| 21 | |
| 22 | impl PrinterWriter<'_> { |
| 23 | pub fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { |
| 24 | self.0.write_fmt(args) |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | pub struct PrinterInner<T: ?Sized = dyn rustyline::ExternalPrinter + Send>(T); |
| 29 | |
| 30 | impl Printer { |
| 31 | /// Makes a new printer. |
| 32 | pub fn new(printer: impl rustyline::ExternalPrinter + Send + 'static) -> Self { |
| 33 | // Use a `LineWriter` internally because each printer `print` call will |
| 34 | // write a full line. This way, as long as no line gets too long and no |
| 35 | // one calls `flush` (which is not exposed), we will always write at |
| 36 | // least one full line to the printer at a time. |
| 37 | Self(Arc::new(Mutex::new(LineWriter::new(PrinterInner(printer))))) |
| 38 | } |
| 39 | |
| 40 | /// Gets the writer. Internally, this takes a lock to ensure that the line |
| 41 | /// is not split across multiple threads, so don't hold it for a long time |
| 42 | /// or across `await`s. |
| 43 | pub fn out(&self) -> PrinterWriter<'_> { |
| 44 | PrinterWriter(self.0.lock()) |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | impl<T: rustyline::ExternalPrinter + ?Sized> Write for PrinterInner<T> { |
| 49 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
| 50 | let s = String::from_utf8_lossy(buf).into_owned(); |
| 51 | self.0.print(s).map_err(std::io::Error::other)?; |
| 52 | |
| 53 | Ok(buf.len()) |
| 54 | } |
| 55 | |
| 56 | fn flush(&mut self) -> std::io::Result<()> { |
| 57 | Ok(()) |
| 58 | } |
| 59 | } |
| 60 | |