desktop: Add log filename pattern (e.g. with timestamp) to preferences

This commit is contained in:
Nathan Adams 2024-03-09 00:38:09 +01:00
parent 6585ffdd28
commit a5fe7e1c81
7 changed files with 211 additions and 1 deletions

View File

@ -13,3 +13,7 @@ language = Language
audio-output-device = Audio Output Device
audio-output-device-default = System Default
log-filename-pattern = Log Filename
log-filename-pattern-single-file = Single File (ruffle.log)
log-filename-pattern-with-timestamp = With Timestamp

View File

@ -1,4 +1,5 @@
use crate::gui::{available_languages, optional_text, text};
use crate::log::FilenamePattern;
use crate::preferences::GlobalPreferences;
use cpal::traits::{DeviceTrait, HostTrait};
use egui::{Align2, Button, ComboBox, Grid, Ui, Widget, Window};
@ -25,6 +26,9 @@ pub struct PreferencesDialog {
output_device: Option<String>,
available_output_devices: Vec<String>,
output_device_changed: bool,
log_filename_pattern: FilenamePattern,
log_filename_pattern_changed: bool,
}
impl PreferencesDialog {
@ -63,6 +67,9 @@ impl PreferencesDialog {
available_output_devices,
output_device_changed: false,
log_filename_pattern: preferences.log_filename_pattern(),
log_filename_pattern_changed: false,
preferences,
}
}
@ -88,6 +95,8 @@ impl PreferencesDialog {
self.show_language_preferences(locale, ui);
self.show_audio_preferences(locale, ui);
self.show_log_preferences(locale, ui);
});
if self.restart_required() {
@ -115,6 +124,7 @@ impl PreferencesDialog {
self.graphics_backend != self.preferences.graphics_backends()
|| self.power_preference != self.preferences.graphics_power_preference()
|| self.output_device != self.preferences.output_device_name()
|| self.log_filename_pattern != self.preferences.log_filename_pattern()
}
fn show_graphics_preferences(
@ -237,6 +247,30 @@ impl PreferencesDialog {
ui.end_row();
}
fn show_log_preferences(&mut self, locale: &LanguageIdentifier, ui: &mut Ui) {
ui.label(text(locale, "log-filename-pattern"));
let previous = self.log_filename_pattern;
ComboBox::from_id_source("log-filename-pattern")
.selected_text(filename_pattern_name(locale, self.log_filename_pattern))
.show_ui(ui, |ui| {
ui.selectable_value(
&mut self.log_filename_pattern,
FilenamePattern::SingleFile,
filename_pattern_name(locale, FilenamePattern::SingleFile),
);
ui.selectable_value(
&mut self.log_filename_pattern,
FilenamePattern::WithTimestamp,
filename_pattern_name(locale, FilenamePattern::WithTimestamp),
);
});
if self.log_filename_pattern != previous {
self.log_filename_pattern_changed = true;
}
ui.end_row();
}
fn save(&mut self) {
if let Err(e) = self.preferences.write_preferences(|preferences| {
if self.graphics_backend_changed {
@ -252,6 +286,9 @@ impl PreferencesDialog {
preferences.set_output_device(self.output_device.clone());
// [NA] TODO: Inform the running player that the device changed
}
if self.log_filename_pattern_changed {
preferences.set_log_filename_pattern(self.log_filename_pattern);
}
}) {
// [NA] TODO: Better error handling... everywhere in desktop, really
tracing::error!("Could not save preferences: {e}");
@ -276,6 +313,13 @@ fn graphics_power_name(locale: &LanguageIdentifier, power_preference: PowerPrefe
}
}
fn filename_pattern_name(locale: &LanguageIdentifier, pattern: FilenamePattern) -> Cow<str> {
match pattern {
FilenamePattern::SingleFile => text(locale, "log-filename-pattern-single-file"),
FilenamePattern::WithTimestamp => text(locale, "log-filename-pattern-with-timestamp"),
}
}
fn backend_availability(descriptors: &Descriptors, backend: wgpu::Backends) -> wgpu::Backends {
if descriptors
.wgpu_instance

40
desktop/src/log.rs Normal file
View File

@ -0,0 +1,40 @@
use chrono::Utc;
use std::path::{Path, PathBuf};
use std::str::FromStr;
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Default)]
pub enum FilenamePattern {
#[default]
SingleFile,
WithTimestamp,
}
impl FromStr for FilenamePattern {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"single_file" => Ok(FilenamePattern::SingleFile),
"with_timestamp" => Ok(FilenamePattern::WithTimestamp),
_ => Err(()),
}
}
}
impl FilenamePattern {
pub fn create_path(&self, directory: &Path) -> PathBuf {
match self {
FilenamePattern::SingleFile => directory.join("ruffle.log"),
FilenamePattern::WithTimestamp => {
directory.join(Utc::now().format("ruffle_%F_%H-%M-%S.log").to_string())
}
}
}
pub fn as_str(&self) -> &'static str {
match self {
FilenamePattern::SingleFile => "single_file",
FilenamePattern::WithTimestamp => "with_timestamp",
}
}
}

View File

@ -12,6 +12,7 @@ mod cli;
mod custom_event;
mod executor;
mod gui;
mod log;
mod player;
mod preferences;
mod task;
@ -155,7 +156,9 @@ fn main() -> Result<(), Error> {
// [NA] `_guard` cannot be `_` or it'll immediately drop
// https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/index.html
let log_path = preferences.cli.config.join("ruffle.log");
let log_path = preferences
.log_filename_pattern()
.create_path(&preferences.cli.config);
let (non_blocking_file, _file_guard) = tracing_appender::non_blocking(File::create(log_path)?);
let (non_blocking_stdout, _stdout_guard) = tracing_appender::non_blocking(std::io::stdout());

View File

@ -2,6 +2,7 @@ mod read;
mod write;
use crate::cli::Opt;
use crate::log::FilenamePattern;
use crate::preferences::read::read_preferences;
use crate::preferences::write::PreferencesWriter;
use anyhow::{Context, Error};
@ -117,6 +118,15 @@ impl GlobalPreferences {
})
}
pub fn log_filename_pattern(&self) -> FilenamePattern {
self.preferences
.lock()
.expect("Preferences is not reentrant")
.values
.log
.filename_pattern
}
pub fn write_preferences(&self, fun: impl FnOnce(&mut PreferencesWriter)) -> Result<(), Error> {
let mut preferences = self
.preferences
@ -158,6 +168,7 @@ pub struct SavedGlobalPreferences {
pub output_device: Option<String>,
pub mute: bool,
pub volume: f32,
pub log: LogPreferences,
}
impl Default for SavedGlobalPreferences {
@ -173,6 +184,12 @@ impl Default for SavedGlobalPreferences {
output_device: None,
mute: false,
volume: 1.0,
log: Default::default(),
}
}
}
#[derive(PartialEq, Debug, Default)]
pub struct LogPreferences {
pub filename_pattern: FilenamePattern,
}

View File

@ -72,6 +72,21 @@ pub fn read_preferences(input: &str) -> (ParseResult, Document) {
Err(e) => result.add_warning(format!("Invalid mute: {e}")),
};
if let Some(log_item) = document.get("log") {
if let Some(log) = log_item.as_table_like() {
match parse_item_from_str(log.get("filename_pattern")) {
Ok(Some(value)) => result.result.log.filename_pattern = value,
Ok(None) => {}
Err(e) => result.add_warning(format!("Invalid log.filename_pattern: {e}")),
};
} else {
result.add_warning(format!(
"Invalid log: expected table but found {}",
log_item.type_name()
));
}
}
(result, document)
}
@ -118,6 +133,8 @@ fn parse_item_from_bool(item: Option<&Item>) -> Result<Option<bool>, String> {
#[cfg(test)]
mod tests {
use super::*;
use crate::log::FilenamePattern;
use crate::preferences::LogPreferences;
use fluent_templates::loader::langid;
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
@ -370,4 +387,63 @@ mod tests {
read_preferences("volume = -1.0").0
);
}
#[test]
fn log_filename() {
assert_eq!(
ParseResult {
result: SavedGlobalPreferences {
log: LogPreferences {
..Default::default()
},
..Default::default()
},
warnings: vec![
"Invalid log.filename_pattern: expected string but found integer".to_string()
]
},
read_preferences("log = {filename_pattern = 5}").0
);
assert_eq!(
ParseResult {
result: SavedGlobalPreferences {
log: LogPreferences {
..Default::default()
},
..Default::default()
},
warnings: vec![
"Invalid log.filename_pattern: unsupported value \"???\"".to_string()
]
},
read_preferences("log = {filename_pattern = \"???\"}").0
);
assert_eq!(
ParseResult {
result: SavedGlobalPreferences {
log: LogPreferences {
filename_pattern: FilenamePattern::WithTimestamp,
},
..Default::default()
},
warnings: vec![]
},
read_preferences("log = {filename_pattern = \"with_timestamp\"}").0
);
}
#[test]
fn log() {
assert_eq!(
ParseResult {
result: SavedGlobalPreferences {
..Default::default()
},
warnings: vec!["Invalid log: expected table but found string".to_string()]
},
read_preferences("log = \"yes\"").0
);
}
}

View File

@ -1,3 +1,4 @@
use crate::log::FilenamePattern;
use crate::preferences::PreferencesAndDocument;
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
use toml_edit::value;
@ -43,11 +44,17 @@ impl<'a> PreferencesWriter<'a> {
self.0.toml_document["volume"] = value(volume as f64);
self.0.values.volume = volume;
}
pub fn set_log_filename_pattern(&mut self, pattern: FilenamePattern) {
self.0.toml_document["log"]["filename_pattern"] = value(pattern.as_str());
self.0.values.log.filename_pattern = pattern;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::log::FilenamePattern;
use crate::preferences::read::read_preferences;
use fluent_templates::loader::langid;
@ -149,4 +156,23 @@ mod tests {
"mute = false\n",
);
}
#[test]
fn set_log_filename_pattern() {
test(
"",
|writer| writer.set_log_filename_pattern(FilenamePattern::WithTimestamp),
"log = { filename_pattern = \"with_timestamp\" }\n",
);
test(
"log = { filename_pattern = \"with_timestamp\" }\n",
|writer| writer.set_log_filename_pattern(FilenamePattern::SingleFile),
"log = { filename_pattern = \"single_file\" }\n",
);
test(
"[log]\nfilename_pattern = \"with_timestamp\"\n",
|writer| writer.set_log_filename_pattern(FilenamePattern::SingleFile),
"[log]\nfilename_pattern = \"single_file\"\n",
);
}
}