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 process_streams: bool,
19}
20
21#[derive(Clone, Default)]
23pub struct Captured(Arc<Mutex<Vec<u8>>>);
24
25impl Captured {
26 pub fn text(&self) -> String {
27 String::from_utf8_lossy(&self.0.lock().unwrap()).into_owned()
28 }
29}
30
31impl Write for Captured {
32 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
33 self.0.lock().unwrap().extend_from_slice(buf);
34 Ok(buf.len())
35 }
36
37 fn flush(&mut self) -> io::Result<()> {
38 Ok(())
39 }
40}
41
42impl Console {
43 pub fn std() -> Console {
44 Console { process_streams: true, ..Console::new(Box::new(io::stdin()), Box::new(io::stdout()), Box::new(io::stderr())) }
45 }
46
47 pub fn new(input: Box<dyn Read + Send>, out: Box<dyn Write + Send>, err: Box<dyn Write + Send>) -> Console {
48 let (input, out, err) = (Arc::new(Mutex::new(input)), Arc::new(Mutex::new(out)), Arc::new(Mutex::new(err)));
49 Console { input, out, err, process_streams: false }
50 }
51
52 pub fn is_process_streams(&self) -> bool {
54 self.process_streams
55 }
56
57 pub fn capture(stdin: &[u8]) -> (Console, Captured, Captured) {
59 let (out, err) = (Captured::default(), Captured::default());
60 let console = Console::new(Box::new(io::Cursor::new(stdin.to_vec())), Box::new(out.clone()), Box::new(err.clone()));
61 (console, out, err)
62 }
63
64 pub fn read_stdin(&self) -> Vec<u8> {
66 let mut bytes = Vec::new();
67 let _ = self.input.lock().unwrap().read_to_end(&mut bytes);
68 bytes
69 }
70
71 pub fn out(&self, text: &str) {
72 write_flush(&self.out, text);
73 }
74
75 pub fn err(&self, text: &str) {
76 write_flush(&self.err, text);
77 }
78
79 pub fn println_out(&self, line: &str) {
80 self.out(&format!("{line}{LINE_SEPARATOR}"));
81 }
82
83 pub fn println_err(&self, line: &str) {
84 self.err(&format!("{line}{LINE_SEPARATOR}"));
85 }
86}
87
88fn write_flush(stream: &Stream, text: &str) {
89 let mut stream = stream.lock().unwrap();
90 let _ = stream.write_all(text.as_bytes()).and_then(|()| stream.flush());
91}
92
93pub enum Sink {
95 Out(Console),
96 Err(Console),
97 File(BufWriter<File>),
98 Buffer(Captured),
99}
100
101pub struct Printer {
103 sink: Sink,
104}
105
106impl Printer {
107 pub fn new(sink: Sink) -> Printer {
108 Printer { sink }
109 }
110
111 pub fn buffer() -> (Printer, Captured) {
113 let captured = Captured::default();
114 (Printer::new(Sink::Buffer(captured.clone())), captured)
115 }
116
117 pub fn print(&mut self, text: &str) {
118 match &mut self.sink {
119 Sink::Out(console) => console.out(text),
120 Sink::Err(console) => console.err(text),
121 Sink::File(file) => {
122 let _ = file.write_all(text.as_bytes());
123 }
124 Sink::Buffer(captured) => {
125 let _ = captured.write_all(text.as_bytes());
126 }
127 }
128 }
129
130 pub fn println(&mut self, line: &str) {
131 self.print(&format!("{line}{LINE_SEPARATOR}"));
132 }
133
134 pub fn close(&mut self) {
135 if let Sink::File(file) = &mut self.sink {
136 let _ = file.flush();
137 }
138 }
139}