Skip to content

Commit

Permalink
add logging settings in UI
Browse files Browse the repository at this point in the history
  • Loading branch information
TicClick committed Oct 6, 2024
1 parent 96b8e77 commit 6edc837
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 69 deletions.
101 changes: 54 additions & 47 deletions src/core/logging.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::{hash_map::Entry, HashMap};
use std::fmt::Write as FmtWrite;
use std::fs::File;
use std::io::Write;
use std::io::Write as IOWrite;
use std::path::{Path, PathBuf};

use steel_core::chat::Message;
Expand Down Expand Up @@ -107,51 +108,9 @@ impl ChatLoggerBackend {
}

fn chat_path(&self, chat_name: &str) -> PathBuf {
self.log_directory.join(chat_name.to_lowercase()).with_extension("log")
}

fn format_message(log_line_format: &str, message: &Message) -> String {
let mut result = String::new();
let mut placeholder = String::new();
let mut in_placeholder = false;

for c in log_line_format.chars() {
match c {
'{' => {
in_placeholder = true;
placeholder.clear();
}
'}' => {
if in_placeholder {
result.push_str(&Self::resolve_placeholder(&placeholder, message));
in_placeholder = false;
} else {
result.push(c);
}
}
_ => {
if in_placeholder {
placeholder.push(c);
} else {
result.push(c);
}
}
}
}

result
}

fn resolve_placeholder(placeholder: &str, message: &Message) -> String {
if let Some(format) = placeholder.strip_prefix("date:") {
message.time.format(format).to_string()
} else {
match placeholder {
"username" => message.username.clone(),
"text" => message.text.clone(),
_ => String::from("{unknown}"),
}
}
self.log_directory
.join(chat_name.to_lowercase())
.with_extension("log")
}

fn log(&mut self, chat_name: String, message: Message) -> std::io::Result<()> {
Expand Down Expand Up @@ -188,7 +147,7 @@ impl ChatLoggerBackend {
}
};

let formatted_message = Self::format_message(&self.log_line_format, &message);
let formatted_message = format_message_for_logging(&self.log_line_format, &message);
if let Err(e) = writeln!(&mut f, "{}", formatted_message) {
log::error!("Failed to append a chat log line for {}: {}", chat_name, e);
return Err(e);
Expand All @@ -204,3 +163,51 @@ impl ChatLoggerBackend {
}
}
}

pub fn format_message_for_logging(log_line_format: &str, message: &Message) -> String {
let mut result = String::new();
let mut placeholder = String::new();
let mut in_placeholder = false;

for c in log_line_format.chars() {
match c {
'{' => {
in_placeholder = true;
placeholder.clear();
}
'}' => {
if in_placeholder {
result.push_str(&resolve_placeholder(&placeholder, message));
in_placeholder = false;
} else {
result.push(c);
}
}
_ => {
if in_placeholder {
placeholder.push(c);
} else {
result.push(c);
}
}
}
}

result
}

fn resolve_placeholder(placeholder: &str, message: &Message) -> String {
if let Some(date_format) = placeholder.strip_prefix("date:") {
let mut buf = String::new();
match write!(&mut buf, "{}", message.time.format(date_format)) {
Ok(_) => buf,
Err(_) => format!("{{date:{}}}", date_format),
}
} else {
match placeholder {
"username" => message.username.clone(),
"text" => message.text.clone(),
_ => String::from("{unknown}"),
}
}
}
67 changes: 45 additions & 22 deletions src/core/os.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,65 @@
use std::path::Path;

#[derive(Debug)]
enum OpenTarget<'opener> {
AppFile(&'opener str),
AppDirectory,
enum RuntimeDirectoryPath<'opener> {
File(&'opener str),
RootDirectory,
Subdirectory(&'opener str),
}

fn open_fs_path_in_explorer(path: &Path, target: &str) {
if let Some(path) = path.to_str() {
let path = path.to_owned();
let (executable, args) = if cfg!(target_os = "windows") {
// let file_arg = format!("/select,{}", log_path);
("explorer.exe", vec![path])
} else if cfg!(target_os = "macos") {
("open", vec![path])
} else {
("xdg-open", vec![path])
};
if let Err(e) = std::process::Command::new(executable).args(&args).spawn() {
log::error!(
"failed to open {target:?} from UI: {e:?} (command line: \"{executable} {args:?})"
);
}
}
}

fn open_app_path(target: OpenTarget) {
fn open_app_path(target: RuntimeDirectoryPath) {
if let Ok(mut path) = std::env::current_exe() {
match target {
OpenTarget::AppFile(file_name) => path.set_file_name(file_name),
OpenTarget::AppDirectory => {
RuntimeDirectoryPath::File(file_name) => path.set_file_name(file_name),
RuntimeDirectoryPath::RootDirectory => {
path = path.parent().unwrap().to_path_buf();
}
}
if let Some(path) = path.to_str() {
let path = path.to_owned();
let (executable, args) = if cfg!(target_os = "windows") {
// let file_arg = format!("/select,{}", log_path);
("explorer.exe", vec![path])
} else if cfg!(target_os = "macos") {
("open", vec![path])
} else {
("xdg-open", vec![path])
};
if let Err(e) = std::process::Command::new(executable).args(&args).spawn() {
log::error!("failed to open {target:?} from UI: {e:?} (command line: \"{executable} {args:?})");
RuntimeDirectoryPath::Subdirectory(subdir) => {
path = path.parent().unwrap().to_path_buf().join(subdir)
}
}
open_fs_path_in_explorer(&path, &format!("{:?}", target));
}
}

pub fn open_runtime_log() {
open_app_path(OpenTarget::AppFile("runtime.log"))
open_app_path(RuntimeDirectoryPath::File("runtime.log"))
}

pub fn open_settings_file() {
open_app_path(OpenTarget::AppFile("settings.yaml"))
open_app_path(RuntimeDirectoryPath::File("settings.yaml"))
}

pub fn open_own_directory() {
open_app_path(OpenTarget::AppDirectory)
open_app_path(RuntimeDirectoryPath::RootDirectory)
}

pub fn open_external_directory(path: &str) {
let d = std::path::Path::new(path);
if d.is_relative() {
open_app_path(RuntimeDirectoryPath::Subdirectory(path));
} else {
open_fs_path_in_explorer(d, path);
}
}

pub fn cleanup_after_update() {
Expand Down
83 changes: 83 additions & 0 deletions src/gui/settings/journal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use eframe::egui::RichText;
use steel_core::chat;

use super::SettingsWindow;
use crate::{
core::{self, logging::format_message_for_logging},
gui::state::UIState,
};

impl SettingsWindow {
pub(super) fn show_logging_tab(&mut self, ui: &mut eframe::egui::Ui, state: &mut UIState) {
ui.vertical(|ui| {
ui.heading("chat logging");
ui.checkbox(
&mut state.settings.journal.chat_events.enabled,
"enable chat logging",
);

ui.horizontal(|ui| {
ui.label("logs directory");
ui.text_edit_singleline(&mut state.settings.journal.chat_events.directory)
.on_hover_text_at_pointer("location of all the log files");

// TODO(logging): Test for presence and refuse to open if it doesn't exist.
if ui.button("open").clicked() {
core::os::open_external_directory(
&mut state.settings.journal.chat_events.directory,
);
}
});

ui.label("format of a single line");
ui.text_edit_multiline(&mut state.settings.journal.chat_events.format);

ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("preview: →").color(ui.visuals().warn_fg_color));
let message =
chat::Message::new_text("WilliamGibson", "I think I left my cyberdeck on");
let formatted_message = format_message_for_logging(
&state.settings.journal.chat_events.format,
&message,
);
ui.label(formatted_message);
ui.label(RichText::new("←").color(ui.visuals().warn_fg_color));
});

ui.collapsing("click to show help", |ui| {
ui.horizontal_wrapped(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;

ui.label("allowed placeholders:\n");

ui.label("- ");
ui.label(RichText::new("{username}").color(ui.style().visuals.warn_fg_color));
ui.label(" - author of the message\n");

ui.label("- ");
ui.label(RichText::new("{text}").color(ui.style().visuals.warn_fg_color));
ui.label(" - message text\n");

ui.label("- ");
ui.label(RichText::new("{date:").color(ui.style().visuals.warn_fg_color));
ui.label(RichText::new("dateformat").color(ui.style().visuals.error_fg_color));
ui.label(RichText::new("}").color(ui.style().visuals.warn_fg_color));
ui.label(" - message date/time, where ");
ui.label(RichText::new("dateformat").color(ui.style().visuals.error_fg_color));
ui.label(" is replaced by a format string. example: ");

ui.label(RichText::new("{date:").color(ui.style().visuals.warn_fg_color));
ui.label(
RichText::new("%Y-%m-%d %H:%M:%S").color(ui.style().visuals.error_fg_color),
);
ui.label(RichText::new("}").color(ui.style().visuals.warn_fg_color));
ui.label(" (");
ui.hyperlink_to("click for more examples", "https://strftime.net");
ui.label(")");
});
});
});

// TODO(logging): Add a setting for logging system events.
}
}
6 changes: 6 additions & 0 deletions src/gui/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod application;
mod chat;
mod journal;
mod notifications;
mod ui;

Expand All @@ -22,6 +23,7 @@ pub enum Tab {
Notifications,
#[cfg(feature = "glass")]
Moderation,
Logging,
}

#[derive(Default)]
Expand Down Expand Up @@ -60,6 +62,8 @@ impl SettingsWindow {

#[cfg(feature = "glass")]
ui.selectable_value(&mut self.active_tab, Tab::Moderation, "moderation");

ui.selectable_value(&mut self.active_tab, Tab::Logging, "logging");
});

ui.separator();
Expand All @@ -72,6 +76,8 @@ impl SettingsWindow {

#[cfg(feature = "glass")]
Tab::Moderation => state.glass.show_ui(ui, &state.settings.ui.theme),

Tab::Logging => self.show_logging_tab(ui, state),
}
});

Expand Down

0 comments on commit 6edc837

Please sign in to comment.