Skip to content

Commit a5fe7e1

Browse files
committed
desktop: Add log filename pattern (e.g. with timestamp) to preferences
1 parent 6585ffd commit a5fe7e1

File tree

7 files changed

+211
-1
lines changed

7 files changed

+211
-1
lines changed

desktop/assets/texts/en-US/preferences_dialog.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ language = Language
1313
1414
audio-output-device = Audio Output Device
1515
audio-output-device-default = System Default
16+
17+
log-filename-pattern = Log Filename
18+
log-filename-pattern-single-file = Single File (ruffle.log)
19+
log-filename-pattern-with-timestamp = With Timestamp

desktop/src/gui/preferences_dialog.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::gui::{available_languages, optional_text, text};
2+
use crate::log::FilenamePattern;
23
use crate::preferences::GlobalPreferences;
34
use cpal::traits::{DeviceTrait, HostTrait};
45
use egui::{Align2, Button, ComboBox, Grid, Ui, Widget, Window};
@@ -25,6 +26,9 @@ pub struct PreferencesDialog {
2526
output_device: Option<String>,
2627
available_output_devices: Vec<String>,
2728
output_device_changed: bool,
29+
30+
log_filename_pattern: FilenamePattern,
31+
log_filename_pattern_changed: bool,
2832
}
2933

3034
impl PreferencesDialog {
@@ -63,6 +67,9 @@ impl PreferencesDialog {
6367
available_output_devices,
6468
output_device_changed: false,
6569

70+
log_filename_pattern: preferences.log_filename_pattern(),
71+
log_filename_pattern_changed: false,
72+
6673
preferences,
6774
}
6875
}
@@ -88,6 +95,8 @@ impl PreferencesDialog {
8895
self.show_language_preferences(locale, ui);
8996

9097
self.show_audio_preferences(locale, ui);
98+
99+
self.show_log_preferences(locale, ui);
91100
});
92101

93102
if self.restart_required() {
@@ -115,6 +124,7 @@ impl PreferencesDialog {
115124
self.graphics_backend != self.preferences.graphics_backends()
116125
|| self.power_preference != self.preferences.graphics_power_preference()
117126
|| self.output_device != self.preferences.output_device_name()
127+
|| self.log_filename_pattern != self.preferences.log_filename_pattern()
118128
}
119129

120130
fn show_graphics_preferences(
@@ -237,6 +247,30 @@ impl PreferencesDialog {
237247
ui.end_row();
238248
}
239249

250+
fn show_log_preferences(&mut self, locale: &LanguageIdentifier, ui: &mut Ui) {
251+
ui.label(text(locale, "log-filename-pattern"));
252+
253+
let previous = self.log_filename_pattern;
254+
ComboBox::from_id_source("log-filename-pattern")
255+
.selected_text(filename_pattern_name(locale, self.log_filename_pattern))
256+
.show_ui(ui, |ui| {
257+
ui.selectable_value(
258+
&mut self.log_filename_pattern,
259+
FilenamePattern::SingleFile,
260+
filename_pattern_name(locale, FilenamePattern::SingleFile),
261+
);
262+
ui.selectable_value(
263+
&mut self.log_filename_pattern,
264+
FilenamePattern::WithTimestamp,
265+
filename_pattern_name(locale, FilenamePattern::WithTimestamp),
266+
);
267+
});
268+
if self.log_filename_pattern != previous {
269+
self.log_filename_pattern_changed = true;
270+
}
271+
ui.end_row();
272+
}
273+
240274
fn save(&mut self) {
241275
if let Err(e) = self.preferences.write_preferences(|preferences| {
242276
if self.graphics_backend_changed {
@@ -252,6 +286,9 @@ impl PreferencesDialog {
252286
preferences.set_output_device(self.output_device.clone());
253287
// [NA] TODO: Inform the running player that the device changed
254288
}
289+
if self.log_filename_pattern_changed {
290+
preferences.set_log_filename_pattern(self.log_filename_pattern);
291+
}
255292
}) {
256293
// [NA] TODO: Better error handling... everywhere in desktop, really
257294
tracing::error!("Could not save preferences: {e}");
@@ -276,6 +313,13 @@ fn graphics_power_name(locale: &LanguageIdentifier, power_preference: PowerPrefe
276313
}
277314
}
278315

316+
fn filename_pattern_name(locale: &LanguageIdentifier, pattern: FilenamePattern) -> Cow<str> {
317+
match pattern {
318+
FilenamePattern::SingleFile => text(locale, "log-filename-pattern-single-file"),
319+
FilenamePattern::WithTimestamp => text(locale, "log-filename-pattern-with-timestamp"),
320+
}
321+
}
322+
279323
fn backend_availability(descriptors: &Descriptors, backend: wgpu::Backends) -> wgpu::Backends {
280324
if descriptors
281325
.wgpu_instance

desktop/src/log.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use chrono::Utc;
2+
use std::path::{Path, PathBuf};
3+
use std::str::FromStr;
4+
5+
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Default)]
6+
pub enum FilenamePattern {
7+
#[default]
8+
SingleFile,
9+
WithTimestamp,
10+
}
11+
12+
impl FromStr for FilenamePattern {
13+
type Err = ();
14+
15+
fn from_str(s: &str) -> Result<Self, Self::Err> {
16+
match s {
17+
"single_file" => Ok(FilenamePattern::SingleFile),
18+
"with_timestamp" => Ok(FilenamePattern::WithTimestamp),
19+
_ => Err(()),
20+
}
21+
}
22+
}
23+
24+
impl FilenamePattern {
25+
pub fn create_path(&self, directory: &Path) -> PathBuf {
26+
match self {
27+
FilenamePattern::SingleFile => directory.join("ruffle.log"),
28+
FilenamePattern::WithTimestamp => {
29+
directory.join(Utc::now().format("ruffle_%F_%H-%M-%S.log").to_string())
30+
}
31+
}
32+
}
33+
34+
pub fn as_str(&self) -> &'static str {
35+
match self {
36+
FilenamePattern::SingleFile => "single_file",
37+
FilenamePattern::WithTimestamp => "with_timestamp",
38+
}
39+
}
40+
}

desktop/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod cli;
1212
mod custom_event;
1313
mod executor;
1414
mod gui;
15+
mod log;
1516
mod player;
1617
mod preferences;
1718
mod task;
@@ -155,7 +156,9 @@ fn main() -> Result<(), Error> {
155156

156157
// [NA] `_guard` cannot be `_` or it'll immediately drop
157158
// https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/index.html
158-
let log_path = preferences.cli.config.join("ruffle.log");
159+
let log_path = preferences
160+
.log_filename_pattern()
161+
.create_path(&preferences.cli.config);
159162
let (non_blocking_file, _file_guard) = tracing_appender::non_blocking(File::create(log_path)?);
160163
let (non_blocking_stdout, _stdout_guard) = tracing_appender::non_blocking(std::io::stdout());
161164

desktop/src/preferences.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod read;
22
mod write;
33

44
use crate::cli::Opt;
5+
use crate::log::FilenamePattern;
56
use crate::preferences::read::read_preferences;
67
use crate::preferences::write::PreferencesWriter;
78
use anyhow::{Context, Error};
@@ -117,6 +118,15 @@ impl GlobalPreferences {
117118
})
118119
}
119120

121+
pub fn log_filename_pattern(&self) -> FilenamePattern {
122+
self.preferences
123+
.lock()
124+
.expect("Preferences is not reentrant")
125+
.values
126+
.log
127+
.filename_pattern
128+
}
129+
120130
pub fn write_preferences(&self, fun: impl FnOnce(&mut PreferencesWriter)) -> Result<(), Error> {
121131
let mut preferences = self
122132
.preferences
@@ -158,6 +168,7 @@ pub struct SavedGlobalPreferences {
158168
pub output_device: Option<String>,
159169
pub mute: bool,
160170
pub volume: f32,
171+
pub log: LogPreferences,
161172
}
162173

163174
impl Default for SavedGlobalPreferences {
@@ -173,6 +184,12 @@ impl Default for SavedGlobalPreferences {
173184
output_device: None,
174185
mute: false,
175186
volume: 1.0,
187+
log: Default::default(),
176188
}
177189
}
178190
}
191+
192+
#[derive(PartialEq, Debug, Default)]
193+
pub struct LogPreferences {
194+
pub filename_pattern: FilenamePattern,
195+
}

desktop/src/preferences/read.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ pub fn read_preferences(input: &str) -> (ParseResult, Document) {
7272
Err(e) => result.add_warning(format!("Invalid mute: {e}")),
7373
};
7474

75+
if let Some(log_item) = document.get("log") {
76+
if let Some(log) = log_item.as_table_like() {
77+
match parse_item_from_str(log.get("filename_pattern")) {
78+
Ok(Some(value)) => result.result.log.filename_pattern = value,
79+
Ok(None) => {}
80+
Err(e) => result.add_warning(format!("Invalid log.filename_pattern: {e}")),
81+
};
82+
} else {
83+
result.add_warning(format!(
84+
"Invalid log: expected table but found {}",
85+
log_item.type_name()
86+
));
87+
}
88+
}
89+
7590
(result, document)
7691
}
7792

@@ -118,6 +133,8 @@ fn parse_item_from_bool(item: Option<&Item>) -> Result<Option<bool>, String> {
118133
#[cfg(test)]
119134
mod tests {
120135
use super::*;
136+
use crate::log::FilenamePattern;
137+
use crate::preferences::LogPreferences;
121138
use fluent_templates::loader::langid;
122139
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
123140

@@ -370,4 +387,63 @@ mod tests {
370387
read_preferences("volume = -1.0").0
371388
);
372389
}
390+
391+
#[test]
392+
fn log_filename() {
393+
assert_eq!(
394+
ParseResult {
395+
result: SavedGlobalPreferences {
396+
log: LogPreferences {
397+
..Default::default()
398+
},
399+
..Default::default()
400+
},
401+
warnings: vec![
402+
"Invalid log.filename_pattern: expected string but found integer".to_string()
403+
]
404+
},
405+
read_preferences("log = {filename_pattern = 5}").0
406+
);
407+
408+
assert_eq!(
409+
ParseResult {
410+
result: SavedGlobalPreferences {
411+
log: LogPreferences {
412+
..Default::default()
413+
},
414+
..Default::default()
415+
},
416+
warnings: vec![
417+
"Invalid log.filename_pattern: unsupported value \"???\"".to_string()
418+
]
419+
},
420+
read_preferences("log = {filename_pattern = \"???\"}").0
421+
);
422+
423+
assert_eq!(
424+
ParseResult {
425+
result: SavedGlobalPreferences {
426+
log: LogPreferences {
427+
filename_pattern: FilenamePattern::WithTimestamp,
428+
},
429+
..Default::default()
430+
},
431+
warnings: vec![]
432+
},
433+
read_preferences("log = {filename_pattern = \"with_timestamp\"}").0
434+
);
435+
}
436+
437+
#[test]
438+
fn log() {
439+
assert_eq!(
440+
ParseResult {
441+
result: SavedGlobalPreferences {
442+
..Default::default()
443+
},
444+
warnings: vec!["Invalid log: expected table but found string".to_string()]
445+
},
446+
read_preferences("log = \"yes\"").0
447+
);
448+
}
373449
}

desktop/src/preferences/write.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::log::FilenamePattern;
12
use crate::preferences::PreferencesAndDocument;
23
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
34
use toml_edit::value;
@@ -43,11 +44,17 @@ impl<'a> PreferencesWriter<'a> {
4344
self.0.toml_document["volume"] = value(volume as f64);
4445
self.0.values.volume = volume;
4546
}
47+
48+
pub fn set_log_filename_pattern(&mut self, pattern: FilenamePattern) {
49+
self.0.toml_document["log"]["filename_pattern"] = value(pattern.as_str());
50+
self.0.values.log.filename_pattern = pattern;
51+
}
4652
}
4753

4854
#[cfg(test)]
4955
mod tests {
5056
use super::*;
57+
use crate::log::FilenamePattern;
5158
use crate::preferences::read::read_preferences;
5259
use fluent_templates::loader::langid;
5360

@@ -149,4 +156,23 @@ mod tests {
149156
"mute = false\n",
150157
);
151158
}
159+
160+
#[test]
161+
fn set_log_filename_pattern() {
162+
test(
163+
"",
164+
|writer| writer.set_log_filename_pattern(FilenamePattern::WithTimestamp),
165+
"log = { filename_pattern = \"with_timestamp\" }\n",
166+
);
167+
test(
168+
"log = { filename_pattern = \"with_timestamp\" }\n",
169+
|writer| writer.set_log_filename_pattern(FilenamePattern::SingleFile),
170+
"log = { filename_pattern = \"single_file\" }\n",
171+
);
172+
test(
173+
"[log]\nfilename_pattern = \"with_timestamp\"\n",
174+
|writer| writer.set_log_filename_pattern(FilenamePattern::SingleFile),
175+
"[log]\nfilename_pattern = \"single_file\"\n",
176+
);
177+
}
152178
}

0 commit comments

Comments
 (0)