ktrs_cli/ktlint/
console.rs1use std::fs::File;
4use std::io::{self, BufWriter, Read, Write};
5use std::sync::{Arc, Mutex};
6
7pub const LINE_SEPARATOR: &str = if cfg!(windows) { "\r\n" } else { "\n" };
9
10type Stream = Arc<Mutex<Box<dyn Write + Send>>>;
11
12#[derive(Clone)]
14pub struct Console {
15 input: Arc<Mutex<Box<dyn Read + Send>>>,
16 out: Stream,
17 err: Stream,
18}
19
20#[derive(Clone, Default)]
22pub struct Captured(Arc<Mutex<Vec<u8>>>);
23
24impl Captured {
25 pub fn text(&self) -> String {
26 String::from_utf8_lossy(&self.0.lock().unwrap()).into_owned()
27 }
28}
29
30impl Write for Captured {
31 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
32 self.0.lock().unwrap().extend_from_slice(buf);
33 Ok(buf.len())
34 }
35
36 fn flush(&mut self) -> io::Result<()> {
37 Ok(())
38 }
39}
40
41impl Console {
42 pub fn std() -> Console {
43 Console::new(Box::new(io::stdin()), Box::new(io::stdout()), Box::new(io::stderr()))
44 }
45
46 pub fn new(input: Box<dyn Read + Send>, out: Box<dyn Write + Send>, err: Box<dyn Write + Send>) -> Console {
47 Console { input: Arc::new(Mutex::new(input)), out: Arc::new(Mutex::new(out)), err: Arc::new(Mutex::new(err)) }
48 }
49
50 pub fn capture(stdin: &[u8]) -> (Console, Captured, Captured) {
52 let (out, err) = (Captured::default(), Captured::default());
53 let console = Console::new(Box::new(io::Cursor::new(stdin.to_vec())), Box::new(out.clone()), Box::new(err.clone()));
54 (console, out, err)
55 }
56
57 pub fn read_stdin(&self) -> Vec<u8> {
59 let mut bytes = Vec::new();
60 let _ = self.input.lock().unwrap().read_to_end(&mut bytes);
61 bytes
62 }
63
64 pub fn out(&self, text: &str) {
65 write_flush(&self.out, text);
66 }
67
68 pub fn err(&self, text: &str) {
69 write_flush(&self.err, text);
70 }
71
72 pub fn println_out(&self, line: &str) {
73 self.out(&format!("{line}{LINE_SEPARATOR}"));
74 }
75
76 pub fn println_err(&self, line: &str) {
77 self.err(&format!("{line}{LINE_SEPARATOR}"));
78 }
79}
80
81fn write_flush(stream: &Stream, text: &str) {
82 let mut stream = stream.lock().unwrap();
83 let _ = stream.write_all(text.as_bytes()).and_then(|()| stream.flush());
84}
85
86pub enum Sink {
88 Out(Console),
89 Err(Console),
90 File(BufWriter<File>),
91 Buffer(Captured),
92}
93
94pub struct Printer {
96 sink: Sink,
97}
98
99impl Printer {
100 pub fn new(sink: Sink) -> Printer {
101 Printer { sink }
102 }
103
104 pub fn buffer() -> (Printer, Captured) {
106 let captured = Captured::default();
107 (Printer::new(Sink::Buffer(captured.clone())), captured)
108 }
109
110 pub fn print(&mut self, text: &str) {
111 match &mut self.sink {
112 Sink::Out(console) => console.out(text),
113 Sink::Err(console) => console.err(text),
114 Sink::File(file) => {
115 let _ = file.write_all(text.as_bytes());
116 }
117 Sink::Buffer(captured) => {
118 let _ = captured.write_all(text.as_bytes());
119 }
120 }
121 }
122
123 pub fn println(&mut self, line: &str) {
124 self.print(&format!("{line}{LINE_SEPARATOR}"));
125 }
126
127 pub fn close(&mut self) {
128 if let Sink::File(file) = &mut self.sink {
129 let _ = file.flush();
130 }
131 }
132}