more refactoring for great beauty

parse-tree
Jordan Orelli 2 years ago
parent cba1004db8
commit 37224f63c5

@ -43,7 +43,7 @@ impl fmt::Display for Event {
let ctrl = if self.ctrl { '⎈' } else { '·' }; let ctrl = if self.ctrl { '⎈' } else { '·' };
let alt = if self.alt { '⎇' } else { '·' }; let alt = if self.alt { '⎇' } else { '·' };
let shift = if self.shift { '⇧'} else { '·' }; let shift = if self.shift { '⇧'} else { '·' };
write!(f, "{} {} {} {} {: >2} {}", down, ctrl, alt, shift, self.code.val, sym) write!(f, "{} {} {} {} {: >3} {}", down, ctrl, alt, shift, self.code.val, sym)
} }
} }
@ -168,6 +168,8 @@ keycodes! {
0x28 DOWN '↓' 0x28 DOWN '↓'
0xA0 LEFT_SHIFT '⇧' 0xA0 LEFT_SHIFT '⇧'
0xA1 RIGHT_SHIFT '⇧' 0xA1 RIGHT_SHIFT '⇧'
0xBE PERIOD '.'
0xDE QUOTE '\''
} }
/* /*

@ -110,6 +110,6 @@ impl Line {
} }
fn show_debug(&self) { fn show_debug(&self) {
debug!(" {: <4} {: >5} {}", self.cursor, self.chars.len(), self.show()); debug!(" {: <5} {: >5} {}", self.cursor, self.chars.len(), self.show());
} }
} }

@ -1,151 +1,21 @@
mod error; mod error;
mod input; mod input;
mod output;
mod key; mod key;
mod line; mod line;
mod log; mod log;
mod output;
mod prompt; mod prompt;
use line::Line; use line::Line;
use prompt::Prompt; use prompt::Prompt;
use std::fs::File; use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write}; use std::io::{Read, Seek, SeekFrom, Write};
use windows::Win32::{
Foundation::HANDLE,
System::Console,
};
use anyhow::{Context, Result};
use crate::{error::Error, log::*};
#[allow(dead_code)]
fn log_output_mode(mode: Console::CONSOLE_MODE) {
// Characters written by the WriteFile or WriteConsole function or echoed by the ReadFile or
// ReadConsole function are parsed for ASCII control sequences, and the correct action is
// performed. Backspace, tab, bell, carriage return, and line feed characters are processed. It
// should be enabled when using control sequences or when ENABLE_VIRTUAL_TERMINAL_PROCESSING is
// set.
if (mode & Console::ENABLE_PROCESSED_OUTPUT).0 > 0 {
debug!("Processed Output: Enabled");
} else {
debug!("Processed Output: Disabled");
}
// When writing with WriteFile or WriteConsole or echoing with ReadFile or ReadConsole, the
// cursor moves to the beginning of the next row when it reaches the end of the current row.
// This causes the rows displayed in the console window to scroll up automatically when the
// cursor advances beyond the last row in the window. It also causes the contents of the
// console screen buffer to scroll up (../discarding the top row of the console screen buffer)
// when the cursor advances beyond the last row in the console screen buffer. If this mode is
// disabled, the last character in the row is overwritten with any subsequent characters.
if (mode & Console::ENABLE_WRAP_AT_EOL_OUTPUT).0 > 0 {
debug!("Wrap at EOL: Enabled");
} else {
debug!("Wrap at EOL: Disabled");
}
// When writing with WriteFile or WriteConsole, characters are parsed for VT100 and similar
// control character sequences that control cursor movement, color/font mode, and other
// operations that can also be performed via the existing Console APIs. For more information,
// see Console Virtual Terminal Sequences.
//
// Ensure ENABLE_PROCESSED_OUTPUT is set when using this flag.
if (mode & Console::ENABLE_VIRTUAL_TERMINAL_PROCESSING).0 > 0 {
debug!("Terminal Processing: Enabled");
} else {
debug!("Terminal Processing: Disabled");
}
// When writing with WriteFile or WriteConsole, this adds an additional state to end-of-line
// wrapping that can delay the cursor move and buffer scroll operations.
//
// Normally when ENABLE_WRAP_AT_EOL_OUTPUT is set and text reaches the end of the line, the
// cursor will immediately move to the next line and the contents of the buffer will scroll up
// by one line. In contrast with this flag set, the cursor does not move to the next line, and
// the scroll operation is not performed. The written character will be printed in the final
// position on the line and the cursor will remain above this character as if
// ENABLE_WRAP_AT_EOL_OUTPUT was off, but the next printable character will be printed as if
// ENABLE_WRAP_AT_EOL_OUTPUT is on. No overwrite will occur. Specifically, the cursor quickly
// advances down to the following line, a scroll is performed if necessary, the character is
// printed, and the cursor advances one more position.
//
// The typical usage of this flag is intended in conjunction with setting
// ENABLE_VIRTUAL_TERMINAL_PROCESSING to better emulate a terminal emulator where writing the
// final character on the screen (../in the bottom right corner) without triggering an
// immediate scroll is the desired behavior.
if (mode & Console::DISABLE_NEWLINE_AUTO_RETURN).0 > 0 {
debug!("Newline Auto Return: Enabled");
} else {
debug!("Newline Auto Return: Disabled");
}
// The APIs for writing character attributes including WriteConsoleOutput and
// WriteConsoleOutputAttribute allow the usage of flags from character attributes to adjust the
// color of the foreground and background of text. Additionally, a range of DBCS flags was
// specified with the COMMON_LVB prefix. Historically, these flags only functioned in DBCS code
// pages for Chinese, Japanese, and Korean languages.
//
// With exception of the leading byte and trailing byte flags, the remaining flags describing
// line drawing and reverse video (../swap foreground and background colors) can be useful for
// other languages to emphasize portions of output.
//
// Setting this console mode flag will allow these attributes to be used in every code page on
// every language.
//
// It is off by default to maintain compatibility with known applications that have
// historically taken advantage of the console ignoring these flags on non-CJK machines to
// store bits in these fields for their own purposes or by accident.
//
// Note that using the ENABLE_VIRTUAL_TERMINAL_PROCESSING mode can result in LVB grid and
// reverse video flags being set while this flag is still off if the attached application
// requests underlining or inverse video via Console Virtual Terminal Sequences.
if (mode & Console::ENABLE_LVB_GRID_WORLDWIDE).0 > 0 {
debug!("LVB Grid: Enabled");
} else {
debug!("LVB Grid: Disabled");
}
}
pub fn stdout_handle() -> Result<HANDLE> { use anyhow::Result;
unsafe {
let handle = Console::GetStdHandle(Console::STD_OUTPUT_HANDLE)
.context("unable to get stdin handle")?;
Ok(handle)
}
}
fn setup_stdout() -> Result<()> { use crate::log::*;
unsafe {
Console::SetConsoleOutputCP(65001);
}
let mut mode = Console::CONSOLE_MODE(0); fn eval(output: &mut output::Writer, cmd: String, args: Vec<&str>) -> Result<bool> {
unsafe {
let handle = stdout_handle()?;
if Console::GetConsoleMode(handle, &mut mode).as_bool() {
// debug!("Stdout details:");
// log_output_mode(mode);
} else {
return Err(Error::last_error().into());
}
}
Ok(())
}
fn newline(stdout: HANDLE) -> Result<()> {
unsafe {
Error::check(Console::WriteConsoleA(
stdout,
"\r\n".as_bytes(),
None,
None,
))?;
}
Ok(())
}
fn eval(cmd: String, args: Vec<&str>) -> Result<bool> {
match cmd.as_str() { match cmd.as_str() {
"pwd" => { "pwd" => {
let pb = std::env::current_dir()?; let pb = std::env::current_dir()?;
@ -200,11 +70,10 @@ fn eval(cmd: String, args: Vec<&str>) -> Result<bool> {
"tail" => { "tail" => {
if args.len() > 0 { if args.len() > 0 {
let fname = args[0]; let fname = args[0];
let stdout = stdout_handle()?;
match File::options().read(true).open(fname) { match File::options().read(true).open(fname) {
Ok(mut f) => { Ok(mut f) => {
_ = f.seek(SeekFrom::End(0)); _ = f.seek(SeekFrom::End(0));
let mut one_byte: [u8; 1] = [0 ; 1]; let mut one_byte: [u8; 1] = [0; 1];
let mut buf: Vec<u8> = vec![]; let mut buf: Vec<u8> = vec![];
loop { loop {
match f.read(&mut one_byte) { match f.read(&mut one_byte) {
@ -212,14 +81,12 @@ fn eval(cmd: String, args: Vec<&str>) -> Result<bool> {
if n == 1 { if n == 1 {
buf.push(one_byte[0]); buf.push(one_byte[0]);
if let Ok(s) = std::str::from_utf8(&buf) { if let Ok(s) = std::str::from_utf8(&buf) {
unsafe { output.write(s.as_bytes())?;
Error::check(Console::WriteConsoleA(stdout, s.as_bytes(), None, None))?;
}
buf.clear(); buf.clear();
} }
} }
}, }
Err(_) => {}, Err(_) => {}
} }
} }
} }
@ -270,15 +137,12 @@ fn main() -> Result<()> {
} }
} }
setup_stdout()?;
let stdout = stdout_handle()?;
let mut line = Line::new(); let mut line = Line::new();
let prompt = Prompt::new(); let prompt = Prompt::new();
let mut input = input::Reader::new()?; let mut input = input::Reader::new()?;
let mut output = output::Writer::new()?; let mut output = output::Writer::new()?;
prompt.print()?; prompt.print(&mut output)?;
info!("» enter"); info!("» enter");
loop { loop {
match input.next()? { match input.next()? {
@ -294,23 +158,27 @@ fn main() -> Result<()> {
info!(" {}", event); info!(" {}", event);
if event.code == key::ENTER { if event.code == key::ENTER {
newline(stdout)?; output.newline()?;
let s = line.pop(); let s = line.pop();
let parts: Vec<&str> = s.split_whitespace().collect(); let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() > 0 { if parts.len() > 0 {
let cmd = parts[0].to_string(); let cmd = parts[0].to_string();
let args = if parts.len() > 1 { parts[1..].to_vec() } else { vec![] }; let args = if parts.len() > 1 {
parts[1..].to_vec()
} else {
vec![]
};
debug!("◇ {} {}", cmd.clone(), args.join(" ")); debug!("◇ {} {}", cmd.clone(), args.join(" "));
match eval(cmd.clone(), args.clone()) { match eval(&mut output, cmd.clone(), args.clone()) {
Ok(true) => info!("▷ {} {}", cmd, args.join(" ")), Ok(true) => info!("▷ {} {}", cmd, args.join(" ")),
Ok(false) => warn!("▷ {} {}", cmd, args.join(" ")), Ok(false) => warn!("▷ {} {}", cmd, args.join(" ")),
Err(e) => { Err(e) => {
error!("▷ {} {} ● {}", cmd, args.join(" "), e); error!("▷ {} {} ● {}", cmd, args.join(" "), e);
println!("error: {}", e); println!("error: {}", e);
},
} }
} }
prompt.print()?; }
prompt.print(&mut output)?;
continue; continue;
} }
@ -321,20 +189,20 @@ fn main() -> Result<()> {
if event.code == key::BACKSPACE { if event.code == key::BACKSPACE {
if line.backspace() { if line.backspace() {
// move cursor back two spaces // move cursor back two spaces
output.write("\x1b[2D")?; output.write(b"\x1b[2D")?;
// output.back(2)?; // output.back(2)?;
let tail = format!("{} ", line.tail()); let tail = format!("{} ", line.tail());
let n = tail.chars().count(); let n = tail.chars().count();
output.write(&tail)?; output.write(tail.as_bytes())?;
// after writing out the tail, rewind by the number of characters in // after writing out the tail, rewind by the number of characters in
// the tail // the tail
if n > 1 { if n > 1 {
let text = format!("\x1b[{}D", n-1); let text = format!("\x1b[{}D", n - 1);
output.write(&text)?; output.write(text.as_bytes())?;
} else { } else {
// honestly I can't remember how I figured this out // honestly I can't remember how I figured this out
output.write(" \x1b[1D")?; output.write(b" \x1b[1D")?;
} }
} }
continue; continue;
@ -351,16 +219,16 @@ fn main() -> Result<()> {
if event.ctrl && event.code == key::J { if event.ctrl && event.code == key::J {
debug!("⎈ j: dot"); debug!("⎈ j: dot");
// red bullet // red bullet
output.write("\x1b[31m\u{2022}\x1b[0m")?; output.write(String::from("\x1b[31m\u{2022}\x1b[0m").as_bytes())?;
continue; continue;
} }
// CTRL-L to clear the screen // CTRL-L to clear the screen
if event.ctrl && event.code == key::L { if event.ctrl && event.code == key::L {
info!("» clear"); info!("» clear");
output.write("\x1b[2J\x1b[0;0H")?; output.write(b"\x1b[2J\x1b[0;0H")?;
prompt.print()?; prompt.print(&mut output)?;
output.write(&line.show())?; output.write(line.show().as_bytes())?;
continue; continue;
} }
@ -371,20 +239,14 @@ fn main() -> Result<()> {
if n > 0 { if n > 0 {
// move left by the number of elements removed // move left by the number of elements removed
let text = format!("\x1b[{}D", n); let text = format!("\x1b[{}D", n);
unsafe { output.write(text.as_bytes())?;
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
}
// draw the elements remaining, followed by a space for each removed // draw the elements remaining, followed by a space for each removed
// element // element
let kept = line.show(); let kept = line.show();
let text = format!("{}{:width$}", kept, "", width = n); let text = format!("{}{:width$}", kept, "", width = n);
unsafe { output.write(text.as_bytes())?;
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
}
let text = format!("\x1b[{}D", n + kept.chars().count()); let text = format!("\x1b[{}D", n + kept.chars().count());
unsafe { output.write(text.as_bytes())?;
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
}
} }
continue; continue;
} }
@ -396,9 +258,7 @@ fn main() -> Result<()> {
if n > 0 { if n > 0 {
// move left by the distance seeked // move left by the distance seeked
let text = format!("\x1b[{}D", n); let text = format!("\x1b[{}D", n);
unsafe { output.write(text.as_bytes())?;
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
}
} }
continue; continue;
} }
@ -410,9 +270,7 @@ fn main() -> Result<()> {
if n > 0 { if n > 0 {
// move right by the distance seeked // move right by the distance seeked
let text = format!("\x1b[{}C", n); let text = format!("\x1b[{}C", n);
unsafe { output.write(text.as_bytes())?;
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
}
} }
continue; continue;
} }
@ -426,20 +284,12 @@ fn main() -> Result<()> {
let n = tail.chars().count(); let n = tail.chars().count();
// write everything from the current line cursor out to the output buffer. // write everything from the current line cursor out to the output buffer.
unsafe { output.write(tail.as_bytes())?;
Error::check(Console::WriteConsoleA( if n > 1 {
stdout,
tail.as_bytes(),
None,
None,
))?;
// if we wrote more than one character, because we weren't at the end, we // if we wrote more than one character, because we weren't at the end, we
// need to rewind the terminal cursor to where it was. // need to rewind the terminal cursor to where it was.
if n > 1 { let text = format!("\x1b[{}D", n - 1);
let text = format!("\x1b[{}D", n-1); output.write(text.as_bytes())?;
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
}
} }
continue; continue;
} }
@ -449,34 +299,28 @@ fn main() -> Result<()> {
input::Event::Left => { input::Event::Left => {
debug!("⛬ ←"); debug!("⛬ ←");
if line.back() { if line.back() {
unsafe { output.write(b"\x1b[D")?; // lol this sucks
let text = "\x1b[D"; // lol this sucks
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
} }
} }
},
input::Event::Right => { input::Event::Right => {
debug!("⛬ →"); debug!("⛬ →");
if line.forward() { if line.forward() {
unsafe { output.write(b"\x1b[C")?; // lol this sucks
let text = "\x1b[C"; // lol this sucks
Error::check(Console::WriteConsoleA(stdout, text.as_bytes(), None, None))?;
}
} }
} }
input::Event::Up => { input::Event::Up => {
debug!("⛬ ↑"); debug!("⛬ ↑");
}, }
input::Event::Down => { input::Event::Down => {
debug!("⛬ ↓"); debug!("⛬ ↓");
}, }
input::Event::Home => {}, input::Event::Home => {}
input::Event::End => {}, input::Event::End => {}
input::Event::Focus(true) => {}, input::Event::Focus(true) => {}
input::Event::Focus(false) => {}, input::Event::Focus(false) => {}
input::Event::Menu(_command_id) => {}, input::Event::Menu(_command_id) => {}
input::Event::Mouse{..} => {}, input::Event::Mouse { .. } => {}
input::Event::Size => {}, input::Event::Size => {}
} }
} }
} }

@ -1,8 +1,98 @@
use crate::{error::Error, log::*};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crate::error::Error; use std::io::{self, Write};
use windows::Win32::{Foundation::{CloseHandle, HANDLE}, System::Console}; use windows::Win32::{
// use std::io; Foundation::{CloseHandle, HANDLE},
System::Console,
};
#[allow(dead_code)]
fn log_output_mode(mode: Console::CONSOLE_MODE) {
// Characters written by the WriteFile or WriteConsole function or echoed by the ReadFile or
// ReadConsole function are parsed for ASCII control sequences, and the correct action is
// performed. Backspace, tab, bell, carriage return, and line feed characters are processed. It
// should be enabled when using control sequences or when ENABLE_VIRTUAL_TERMINAL_PROCESSING is
// set.
if (mode & Console::ENABLE_PROCESSED_OUTPUT).0 > 0 {
debug!("Processed Output: Enabled");
} else {
debug!("Processed Output: Disabled");
}
// When writing with WriteFile or WriteConsole or echoing with ReadFile or ReadConsole, the
// cursor moves to the beginning of the next row when it reaches the end of the current row.
// This causes the rows displayed in the console window to scroll up automatically when the
// cursor advances beyond the last row in the window. It also causes the contents of the
// console screen buffer to scroll up (../discarding the top row of the console screen buffer)
// when the cursor advances beyond the last row in the console screen buffer. If this mode is
// disabled, the last character in the row is overwritten with any subsequent characters.
if (mode & Console::ENABLE_WRAP_AT_EOL_OUTPUT).0 > 0 {
debug!("Wrap at EOL: Enabled");
} else {
debug!("Wrap at EOL: Disabled");
}
// When writing with WriteFile or WriteConsole, characters are parsed for VT100 and similar
// control character sequences that control cursor movement, color/font mode, and other
// operations that can also be performed via the existing Console APIs. For more information,
// see Console Virtual Terminal Sequences.
//
// Ensure ENABLE_PROCESSED_OUTPUT is set when using this flag.
if (mode & Console::ENABLE_VIRTUAL_TERMINAL_PROCESSING).0 > 0 {
debug!("Terminal Processing: Enabled");
} else {
debug!("Terminal Processing: Disabled");
}
// When writing with WriteFile or WriteConsole, this adds an additional state to end-of-line
// wrapping that can delay the cursor move and buffer scroll operations.
//
// Normally when ENABLE_WRAP_AT_EOL_OUTPUT is set and text reaches the end of the line, the
// cursor will immediately move to the next line and the contents of the buffer will scroll up
// by one line. In contrast with this flag set, the cursor does not move to the next line, and
// the scroll operation is not performed. The written character will be printed in the final
// position on the line and the cursor will remain above this character as if
// ENABLE_WRAP_AT_EOL_OUTPUT was off, but the next printable character will be printed as if
// ENABLE_WRAP_AT_EOL_OUTPUT is on. No overwrite will occur. Specifically, the cursor quickly
// advances down to the following line, a scroll is performed if necessary, the character is
// printed, and the cursor advances one more position.
//
// The typical usage of this flag is intended in conjunction with setting
// ENABLE_VIRTUAL_TERMINAL_PROCESSING to better emulate a terminal emulator where writing the
// final character on the screen (../in the bottom right corner) without triggering an
// immediate scroll is the desired behavior.
if (mode & Console::DISABLE_NEWLINE_AUTO_RETURN).0 > 0 {
debug!("Newline Auto Return: Enabled");
} else {
debug!("Newline Auto Return: Disabled");
}
// The APIs for writing character attributes including WriteConsoleOutput and
// WriteConsoleOutputAttribute allow the usage of flags from character attributes to adjust the
// color of the foreground and background of text. Additionally, a range of DBCS flags was
// specified with the COMMON_LVB prefix. Historically, these flags only functioned in DBCS code
// pages for Chinese, Japanese, and Korean languages.
//
// With exception of the leading byte and trailing byte flags, the remaining flags describing
// line drawing and reverse video (../swap foreground and background colors) can be useful for
// other languages to emphasize portions of output.
//
// Setting this console mode flag will allow these attributes to be used in every code page on
// every language.
//
// It is off by default to maintain compatibility with known applications that have
// historically taken advantage of the console ignoring these flags on non-CJK machines to
// store bits in these fields for their own purposes or by accident.
//
// Note that using the ENABLE_VIRTUAL_TERMINAL_PROCESSING mode can result in LVB grid and
// reverse video flags being set while this flag is still off if the attached application
// requests underlining or inverse video via Console Virtual Terminal Sequences.
if (mode & Console::ENABLE_LVB_GRID_WORLDWIDE).0 > 0 {
debug!("LVB Grid: Enabled");
} else {
debug!("LVB Grid: Disabled");
}
}
pub fn stdout_handle() -> Result<HANDLE> { pub fn stdout_handle() -> Result<HANDLE> {
unsafe { unsafe {
@ -12,41 +102,58 @@ pub fn stdout_handle() -> Result<HANDLE> {
} }
} }
fn setup_stdout() -> Result<()> {
unsafe {
Console::SetConsoleOutputCP(65001);
}
let mut mode = Console::CONSOLE_MODE(0);
unsafe {
let handle = stdout_handle()?;
if Console::GetConsoleMode(handle, &mut mode).as_bool() {
// debug!("Stdout details:");
// log_output_mode(mode);
} else {
return Err(Error::last_error().into());
}
}
Ok(())
}
pub struct Writer { pub struct Writer {
output: HANDLE, output: HANDLE,
} }
impl Writer { impl Writer {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
Ok(Self{ setup_stdout()?;
Ok(Self {
output: stdout_handle()?, output: stdout_handle()?,
}) })
} }
pub fn write(&mut self, s: &str) -> Result<()> { pub fn close(&mut self) -> Result<()> {
unsafe { unsafe {
Error::check(Console::WriteConsoleA(self.output, s.as_bytes(), None, None))?; CloseHandle(self.output);
} }
Ok(()) Ok(())
} }
pub fn close(&mut self) -> Result<()> { pub fn newline(&mut self) -> Result<()> {
self.write(b"\r\n")?;
Ok(())
}
}
impl Write for Writer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { unsafe {
CloseHandle(self.output); Error::check(Console::WriteConsoleA(self.output, buf, None, None))?;
} }
Ok(0)
}
fn flush(&mut self) -> io::Result<()> {
Ok(()) Ok(())
} }
} }
// impl std::io::Write for Writer {
// fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
// unsafe {
// Error::check(Console::WriteConsoleA(self.output, buf, None, None))?;
// }
// Ok(0)
// }
//
// fn flush(&mut self) -> io::Result<()> {
// Ok(())
// }
// }

@ -1,6 +1,6 @@
use crate::{error::Error, stdout_handle}; use crate::output;
use std::io::Write;
use anyhow::Result; use anyhow::Result;
use windows::Win32::System::Console;
pub struct Prompt { pub struct Prompt {
s: String, s: String,
@ -13,24 +13,14 @@ impl Prompt {
} }
} }
pub fn print(&self) -> Result<()> { pub fn print(&self, output: &mut output::Writer) -> Result<()> {
match std::env::current_dir() { match std::env::current_dir() {
Ok(d) => unsafe { Ok(d) => {
let text = d.to_str().unwrap().to_owned() + " " + &self.s; let text = d.to_str().unwrap().to_owned() + " " + &self.s;
Error::check(Console::WriteConsoleA( output.write(text.as_bytes())?;
stdout_handle()?,
text.as_bytes(),
None,
None,
))?;
} }
Err(_) => unsafe { Err(_) => {
Error::check(Console::WriteConsoleA( output.write(self.s.as_bytes())?;
stdout_handle()?,
self.s.as_bytes(),
None,
None,
))?;
}, },
} }
Ok(()) Ok(())

Loading…
Cancel
Save