From 5ddf95a16c8d1c9632ba6cef56f55fcf8bf9e4ea Mon Sep 17 00:00:00 2001 From: Roee Shapira <35409124+5c077m4n@users.noreply.github.com> Date: Thu, 19 Nov 2020 12:15:59 +0200 Subject: [PATCH] fix(compatibility): some fixes for htop (#53) * Moved all log to file functions to one module. * Moved logging to file function to utils module. * Housekeeping. * Housekeeping. * Started investigation into htop CSIs. * Used better function for scrolling. * Minor cleanup. * Cleanup and start of scroll region rotation. * Implemented scroll region rotation. * Improved performace. * New SCI found. * Typo. * Typo. * Removed logging function. * Added scroll rotation functions. * Typo. * Moved all logging function to one module. * Attempt at making htop work. * Reverted unneeded changes. * Improved log file name creation. * Ran rust fmt. * PR review edits. * PR review edits. * Used mark_for_rerender function. * Removed _ prefix from logging functions. --- src/boundaries.rs | 12 ---- src/layout.rs | 12 ---- src/main.rs | 38 ++++------- src/os_input_output.rs | 12 ---- src/pty_bus.rs | 35 +--------- src/screen.rs | 18 +---- src/terminal_pane/scroll.rs | 58 +++++++++++++---- src/terminal_pane/terminal_pane.rs | 101 +++++++++++++++++++---------- src/utils/consts.rs | 3 + src/utils/logging.rs | 58 +++++++++++++++++ src/utils/mod.rs | 2 + 11 files changed, 188 insertions(+), 161 deletions(-) create mode 100644 src/utils/consts.rs create mode 100644 src/utils/logging.rs create mode 100644 src/utils/mod.rs diff --git a/src/boundaries.rs b/src/boundaries.rs index 436d767b..9f414f1c 100644 --- a/src/boundaries.rs +++ b/src/boundaries.rs @@ -1,17 +1,5 @@ use std::collections::HashMap; -fn _debug_log_to_file(message: String) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); -} - pub mod boundary_type { pub const TOP_RIGHT: &str = "┐"; pub const VERTICAL: &str = "│"; diff --git a/src/layout.rs b/src/layout.rs index e80c76b2..f4297bfd 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,18 +1,6 @@ use crate::terminal_pane::PositionAndSize; use serde::{Deserialize, Serialize}; -fn _debug_log_to_file(message: String) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); -} - fn split_space_to_parts_vertically( space_to_split: &PositionAndSize, percentages: Vec, diff --git a/src/main.rs b/src/main.rs index 315e8eb1..d331451c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ -#[cfg(test)] -mod tests; - mod boundaries; mod layout; mod os_input_output; mod pty_bus; mod screen; mod terminal_pane; +#[cfg(test)] +mod tests; +mod utils; use std::io::{Read, Write}; use std::os::unix::net::UnixStream; @@ -22,6 +22,7 @@ use crate::layout::Layout; use crate::os_input_output::{get_os_input, OsApi}; use crate::pty_bus::{PtyBus, PtyInstruction, VteEvent}; use crate::screen::{Screen, ScreenInstruction}; +use crate::utils::{consts::MOSAIC_TMP_FOLDER, logging::*}; #[derive(Serialize, Deserialize, Debug)] enum ApiCommand { @@ -53,46 +54,28 @@ pub struct Opt { debug: bool, } -fn _debug_log_to_file(message: String) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); -} - -fn delete_log_files() -> std::io::Result<()> { - std::fs::remove_dir_all("/tmp/mosaic-logs").ok(); - std::fs::create_dir_all("/tmp/mosaic-logs").ok(); - Ok(()) -} - pub fn main() { let opts = Opt::from_args(); if opts.split.is_some() { match opts.split { Some('h') => { - let mut stream = UnixStream::connect("/tmp/mosaic").unwrap(); + let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap(); let api_command = bincode::serialize(&ApiCommand::SplitHorizontally).unwrap(); stream.write_all(&api_command).unwrap(); } Some('v') => { - let mut stream = UnixStream::connect("/tmp/mosaic").unwrap(); + let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap(); let api_command = bincode::serialize(&ApiCommand::SplitVertically).unwrap(); stream.write_all(&api_command).unwrap(); } _ => {} }; } else if opts.move_focus { - let mut stream = UnixStream::connect("/tmp/mosaic").unwrap(); + let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap(); let api_command = bincode::serialize(&ApiCommand::MoveFocus).unwrap(); stream.write_all(&api_command).unwrap(); } else if opts.open_file.is_some() { - let mut stream = UnixStream::connect("/tmp/mosaic").unwrap(); + let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap(); let file_to_open = opts.open_file.unwrap(); let api_command = bincode::serialize(&ApiCommand::OpenFile(file_to_open)).unwrap(); stream.write_all(&api_command).unwrap(); @@ -109,7 +92,8 @@ pub enum AppInstruction { pub fn start(mut os_input: Box, opts: Opt) { let mut active_threads = vec![]; - delete_log_files().unwrap(); + delete_log_dir().unwrap(); + delete_log_file().unwrap(); let full_screen_ws = os_input.get_terminal_size_using_fd(0); os_input.into_raw_mode(0); @@ -335,7 +319,7 @@ pub fn start(mut os_input: Box, opts: Opt) { let mut buffer = [0; 10]; // TODO: more accurately stdin.read(&mut buffer).expect("failed to read stdin"); // uncomment this to print the entered character to a log file (/tmp/mosaic-log.txt) for debugging - // _debug_log_to_file(format!("buffer {:?}", buffer)); + //crate::utils::logging::debug_log_to_file(format!("buffer {:?}", buffer)); match buffer { [10, 0, 0, 0, 0, 0, 0, 0, 0, 0] => { // ctrl-j diff --git a/src/os_input_output.rs b/src/os_input_output.rs index 251b8c5e..ecd18edc 100644 --- a/src/os_input_output.rs +++ b/src/os_input_output.rs @@ -58,18 +58,6 @@ pub fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) { unsafe { ioctl(fd, TIOCSWINSZ.into(), &winsize) }; } -fn _debug_log_to_file(message: String) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); -} - fn handle_command_exit(mut child: Child) { let signals = ::signal_hook::iterator::Signals::new(&[::signal_hook::SIGINT]).unwrap(); 'handle_exit: loop { diff --git a/src/pty_bus.rs b/src/pty_bus.rs index fed8f7f7..49334736 100644 --- a/src/pty_bus.rs +++ b/src/pty_bus.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; use crate::layout::Layout; use crate::os_input_output::OsApi; +use crate::utils::logging::debug_to_file; use crate::ScreenInstruction; pub struct ReadFromPid { @@ -27,38 +28,6 @@ impl ReadFromPid { } } -fn _debug_log_to_file(message: String) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); -} - -fn debug_to_file(message: u8, pid: RawFd) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut path = PathBuf::new(); - path.push( - [ - String::from("/tmp/mosaic-logs/mosaic-"), - pid.to_string(), - String::from(".log"), - ] - .concat(), - ); - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open(path) - .unwrap(); - file.write_all(&[message]).unwrap(); -} - impl Stream for ReadFromPid { type Item = Vec; fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { @@ -208,7 +177,7 @@ fn stream_terminal_bytes( let bytes_is_empty = bytes.is_empty(); for byte in bytes { if debug { - debug_to_file(byte, pid); + debug_to_file(byte, pid).unwrap(); } vte_parser.advance(&mut vte_event_sender, byte); } diff --git a/src/screen.rs b/src/screen.rs index cf67909c..76c7b182 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -19,19 +19,7 @@ use crate::AppInstruction; * */ -fn _debug_log_to_file(message: String) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); -} - -const CURSOR_HEIGHT_WIDGH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this +const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this type BorderAndPaneIds = (usize, Vec); @@ -206,7 +194,7 @@ impl Screen { (0, 0), |(current_longest_edge, current_terminal_id_to_split), id_and_terminal_to_check| { let (id_of_terminal_to_check, terminal_to_check) = id_and_terminal_to_check; - let terminal_size = (terminal_to_check.get_rows() * CURSOR_HEIGHT_WIDGH_RATIO) + let terminal_size = (terminal_to_check.get_rows() * CURSOR_HEIGHT_WIDTH_RATIO) * terminal_to_check.get_columns(); if terminal_size > current_longest_edge { (terminal_size, *id_of_terminal_to_check) @@ -222,7 +210,7 @@ impl Screen { ws_xpixel: terminal_to_split.get_x() as u16, ws_ypixel: terminal_to_split.get_y() as u16, }; - if terminal_to_split.get_rows() * CURSOR_HEIGHT_WIDGH_RATIO + if terminal_to_split.get_rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.get_columns() { let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws); diff --git a/src/terminal_pane/scroll.rs b/src/terminal_pane/scroll.rs index 3ced3828..5884ea32 100644 --- a/src/terminal_pane/scroll.rs +++ b/src/terminal_pane/scroll.rs @@ -26,18 +26,6 @@ pub struct CanonicalLine { pub wrapped_fragments: Vec, } -fn debug_log_to_file(message: String) { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); -} - impl CanonicalLine { pub fn new() -> Self { CanonicalLine { @@ -163,6 +151,9 @@ impl CursorPosition { pub fn move_to_next_canonical_line(&mut self) { self.line_index.0 += 1; } + pub fn move_to_prev_canonical_line(&mut self) { + self.line_index.0 -= 1; + } pub fn move_to_beginning_of_linewrap(&mut self) { self.column_index = 0; } @@ -307,8 +298,7 @@ impl Scroll { } } if current_canonical_line_index == self.canonical_lines.len() - 1 { - let new_canonical_line = CanonicalLine::new(); - self.canonical_lines.push(new_canonical_line); + self.canonical_lines.push(CanonicalLine::new()); self.cursor_position.move_to_next_canonical_line(); self.cursor_position.move_to_beginning_of_canonical_line(); } else if current_canonical_line_index < self.canonical_lines.len() - 1 { @@ -517,6 +507,46 @@ impl Scroll { } } } + /// [scroll_up](https://github.com/alacritty/alacritty/blob/ec42b42ce601808070462111c0c28edb0e89babb/alacritty_terminal/src/grid/mod.rs#L261) + /// This function takes the first line of the scroll region and moves it to the bottom (count times) + pub fn rotate_scroll_region_up(&mut self, count: usize) { + if let Some((_, scroll_region_bottom)) = self.scroll_region { + if self.show_cursor { + let scroll_region_bottom_index = scroll_region_bottom - 1; + self.cursor_position + .move_to_canonical_line(scroll_region_bottom_index); + + let new_empty_lines = vec![CanonicalLine::new(); count]; + self.canonical_lines.splice( + scroll_region_bottom_index..scroll_region_bottom_index + 1, + new_empty_lines, + ); + + self.cursor_position + .move_to_canonical_line(scroll_region_bottom_index + count); + } + } + } + /// [scroll_down](https://github.com/alacritty/alacritty/blob/ec42b42ce601808070462111c0c28edb0e89babb/alacritty_terminal/src/grid/mod.rs#L221) + /// This function takes the last line of the scroll region and moves it to the top (count times) + pub fn rotate_scroll_region_down(&mut self, count: usize) { + if let Some((scroll_region_top, _)) = self.scroll_region { + if self.show_cursor { + let scroll_region_top_index = scroll_region_top - 1; + self.cursor_position + .move_to_canonical_line(scroll_region_top_index); + + let new_empty_lines = vec![CanonicalLine::new(); count]; + self.canonical_lines.splice( + scroll_region_top_index..scroll_region_top_index, + new_empty_lines, + ); + + self.cursor_position + .move_to_canonical_line(scroll_region_top_index + count); + } + } + } pub fn move_viewport_up(&mut self, count: usize) { if let Some(current_offset) = self.viewport_bottom_offset.as_mut() { *current_offset += count; diff --git a/src/terminal_pane/terminal_pane.rs b/src/terminal_pane/terminal_pane.rs index fc1cc207..bdaf6417 100644 --- a/src/terminal_pane/terminal_pane.rs +++ b/src/terminal_pane/terminal_pane.rs @@ -7,6 +7,7 @@ use crate::terminal_pane::terminal_character::{ AnsiCode, CharacterStyles, NamedColor, TerminalCharacter, }; use crate::terminal_pane::Scroll; +use crate::utils::logging::{debug_log_to_file, debug_log_to_file_pid_0}; use crate::VteEvent; #[derive(Clone, Copy, Debug)] @@ -61,11 +62,14 @@ impl TerminalPane { position_and_size_override: None, } } + pub fn mark_for_rerender(&mut self) { + self.should_render = true; + } pub fn handle_event(&mut self, event: VteEvent) { match event { VteEvent::Print(c) => { self.print(c); - self.should_render = true; + self.mark_for_rerender(); } VteEvent::Execute(byte) => { self.execute(byte); @@ -95,57 +99,57 @@ impl TerminalPane { self.position_and_size.x += count; self.position_and_size.columns -= count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn reduce_width_left(&mut self, count: usize) { self.position_and_size.columns -= count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn increase_width_left(&mut self, count: usize) { self.position_and_size.x -= count; self.position_and_size.columns += count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn increase_width_right(&mut self, count: usize) { self.position_and_size.columns += count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn reduce_height_down(&mut self, count: usize) { self.position_and_size.y += count; self.position_and_size.rows -= count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn increase_height_down(&mut self, count: usize) { self.position_and_size.rows += count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn increase_height_up(&mut self, count: usize) { self.position_and_size.y -= count; self.position_and_size.rows += count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn reduce_height_up(&mut self, count: usize) { self.position_and_size.rows -= count; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn change_size_p(&mut self, position_and_size: &PositionAndSize) { self.position_and_size = *position_and_size; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } // TODO: merge these two methods pub fn change_size(&mut self, ws: &Winsize) { self.position_and_size.columns = ws.ws_col as usize; self.position_and_size.rows = ws.ws_row as usize; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn get_x(&self) -> usize { match self.position_and_size_override { @@ -210,7 +214,7 @@ impl TerminalPane { } character_styles.clear(); } - self.should_render = false; + self.mark_for_rerender(); Some(vte_output) } else { None @@ -225,15 +229,23 @@ impl TerminalPane { } pub fn scroll_up(&mut self, count: usize) { self.scroll.move_viewport_up(count); - self.should_render = true; + self.mark_for_rerender(); } pub fn scroll_down(&mut self, count: usize) { self.scroll.move_viewport_down(count); - self.should_render = true; + self.mark_for_rerender(); + } + pub fn rotate_scroll_region_up(&mut self, count: usize) { + self.scroll.rotate_scroll_region_up(count); + self.mark_for_rerender(); + } + pub fn rotate_scroll_region_down(&mut self, count: usize) { + self.scroll.rotate_scroll_region_down(count); + self.mark_for_rerender(); } pub fn clear_scroll(&mut self) { self.scroll.reset_viewport(); - self.should_render = true; + self.mark_for_rerender(); } pub fn override_size_and_position(&mut self, x: usize, y: usize, size: &Winsize) { let position_and_size_override = PositionAndSize { @@ -244,17 +256,17 @@ impl TerminalPane { }; self.position_and_size_override = Some(position_and_size_override); self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } pub fn reset_size_and_position_override(&mut self) { self.position_and_size_override = None; self.reflow_lines(); - self.should_render = true; + self.mark_for_rerender(); } fn add_newline(&mut self) { self.scroll.add_canonical_line(); // self.reset_all_ansi_codes(); // TODO: find out if we should be resetting here or not - self.should_render = true; + self.mark_for_rerender(); } fn move_to_beginning_of_line(&mut self) { self.scroll.move_cursor_to_beginning_of_linewrap(); @@ -267,20 +279,6 @@ impl TerminalPane { } } -fn debug_log_to_file(message: String, pid: RawFd) { - if pid == 3 { - use std::fs::OpenOptions; - use std::io::prelude::*; - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open("/tmp/mosaic-log.txt") - .unwrap(); - file.write_all(message.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); - } -} - impl vte::Perform for TerminalPane { fn print(&mut self, c: char) { // apparently, building TerminalCharacter like this without a "new" method @@ -632,7 +630,8 @@ impl vte::Perform for TerminalPane { .pending_styles .background(Some(AnsiCode::NamedColor(NamedColor::White))); } else { - debug_log_to_file(format!("unhandled csi m code {:?}", params), self.pid); + debug_log_to_file_pid_0(format!("unhandled csi m code {:?}", params), self.pid) + .unwrap(); } } else if c == 'C' { // move cursor forward @@ -685,7 +684,7 @@ impl vte::Perform for TerminalPane { match params.get(0) { Some(25) => { self.scroll.hide_cursor(); - self.should_render = true; + self.mark_for_rerender(); } _ => {} }; @@ -700,7 +699,7 @@ impl vte::Perform for TerminalPane { match params.get(0) { Some(25) => { self.scroll.show_cursor(); - self.should_render = true; + self.mark_for_rerender(); } _ => {} }; @@ -741,8 +740,38 @@ impl vte::Perform for TerminalPane { .add_empty_lines_in_scroll_region(line_count_to_add); } else if c == 'q' || c == 'd' || c == 'X' || c == 'G' { // ignore for now to run on mac + } else if c == 'T' { + /* + * 124 54 T SD + * Scroll down, new lines inserted at top of screen + * [4T = Scroll down 4, bring previous lines back into view + */ + debug_log_to_file(format!( + "htop (only?) linux csi: {}->{:?} ({:?} - ignore: {})", + c, params, _intermediates, _ignore + )) + .unwrap(); + let line_count: i64 = *params.get(0).expect("A number of lines was expected."); + + if line_count >= 0 { + self.rotate_scroll_region_up(line_count as usize); + } else { + self.rotate_scroll_region_down(line_count.abs() as usize); + } + } else if c == 'P' { + /* + * 120 50 P * DCH + * Delete Character, from current position to end of field + * [4P = Delete 4 characters, VT102 series + */ + debug_log_to_file(format!( + "htop (only?) linux csi: {}->{:?} (intermediates: {:?}, ignore: {})", + c, params, _intermediates, _ignore + )) + .unwrap(); } else { - panic!("unhandled csi: {:?}->{:?}", c, params); + debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params)).unwrap(); + panic!("unhandled csi: {}->{:?}", c, params); } } diff --git a/src/utils/consts.rs b/src/utils/consts.rs new file mode 100644 index 00000000..5eb96c67 --- /dev/null +++ b/src/utils/consts.rs @@ -0,0 +1,3 @@ +pub const MOSAIC_TMP_LOG_FILE: &str = "/tmp/mosaic-log.txt"; +pub const MOSAIC_TMP_LOG_DIR: &str = "/tmp/mosaic-log"; +pub const MOSAIC_TMP_FOLDER: &str = "/tmp/mosaic"; diff --git a/src/utils/logging.rs b/src/utils/logging.rs new file mode 100644 index 00000000..77e924a9 --- /dev/null +++ b/src/utils/logging.rs @@ -0,0 +1,58 @@ +use std::{ + fs, + io::{self, prelude::*}, + os::unix::io::RawFd, + path::PathBuf, +}; + +use crate::utils::consts::{MOSAIC_TMP_LOG_DIR, MOSAIC_TMP_LOG_FILE}; + +pub fn debug_log_to_file(message: String) -> io::Result<()> { + let mut file = fs::OpenOptions::new() + .append(true) + .create(true) + .open(MOSAIC_TMP_LOG_FILE)?; + file.write_all(message.as_bytes())?; + file.write_all("\n".as_bytes())?; + + Ok(()) +} + +pub fn debug_log_to_file_pid_0(message: String, pid: RawFd) -> io::Result<()> { + if pid == 0 { + debug_log_to_file(message)?; + } + + Ok(()) +} + +pub fn delete_log_file() -> io::Result<()> { + if fs::metadata(MOSAIC_TMP_LOG_FILE).is_ok() { + fs::remove_file(MOSAIC_TMP_LOG_FILE)?; + } + + Ok(()) +} + +pub fn delete_log_dir() -> io::Result<()> { + if fs::metadata(MOSAIC_TMP_LOG_DIR).is_ok() { + fs::remove_dir_all(MOSAIC_TMP_LOG_DIR)?; + } + fs::create_dir_all(MOSAIC_TMP_LOG_DIR)?; + + Ok(()) +} + +pub fn debug_to_file(message: u8, pid: RawFd) -> io::Result<()> { + let mut path = PathBuf::new(); + path.push(MOSAIC_TMP_LOG_DIR); + path.push(format!("mosaic-{}.log", pid.to_string())); + + let mut file = fs::OpenOptions::new() + .append(true) + .create(true) + .open(path)?; + file.write_all(&[message])?; + + Ok(()) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 00000000..140eec18 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod consts; +pub mod logging;