From 2cd915046edb4cf20a63bf2e5404102787328c03 Mon Sep 17 00:00:00 2001 From: sleepycatcoding <131554884+sleepycatcoding@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:21:25 +0300 Subject: [PATCH] frontend-utils: Move bookmark writer from desktop --- desktop/src/preferences.rs | 4 +- desktop/src/preferences/write.rs | 387 ++++++++------------------ frontend-utils/src/bookmarks.rs | 2 + frontend-utils/src/bookmarks/write.rs | 142 ++++++++++ frontend-utils/src/write.rs | 23 ++ 5 files changed, 279 insertions(+), 279 deletions(-) create mode 100644 frontend-utils/src/bookmarks/write.rs diff --git a/desktop/src/preferences.rs b/desktop/src/preferences.rs index f68218982..25a6ca48d 100644 --- a/desktop/src/preferences.rs +++ b/desktop/src/preferences.rs @@ -6,10 +6,10 @@ pub mod storage; use crate::cli::Opt; use crate::log::FilenamePattern; use crate::preferences::read::read_preferences; -use crate::preferences::write::{BookmarksWriter, PreferencesWriter}; +use crate::preferences::write::PreferencesWriter; use anyhow::{Context, Error}; use ruffle_core::backend::ui::US_ENGLISH; -use ruffle_frontend_utils::bookmarks::{read_bookmarks, Bookmarks}; +use ruffle_frontend_utils::bookmarks::{read_bookmarks, Bookmarks, BookmarksWriter}; use ruffle_frontend_utils::parse::DocumentHolder; use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference}; use std::sync::{Arc, Mutex}; diff --git a/desktop/src/preferences/write.rs b/desktop/src/preferences/write.rs index 2d649b382..d84314cda 100644 --- a/desktop/src/preferences/write.rs +++ b/desktop/src/preferences/write.rs @@ -1,11 +1,9 @@ use crate::log::FilenamePattern; use crate::preferences::storage::StorageBackend; use crate::preferences::SavedGlobalPreferences; -use ruffle_frontend_utils::bookmarks::{Bookmark, Bookmarks}; use ruffle_frontend_utils::parse::DocumentHolder; -use ruffle_frontend_utils::write::TableExt; use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference}; -use toml_edit::{value, ArrayOfTables, Table}; +use toml_edit::value; use unic_langid::LanguageIdentifier; pub struct PreferencesWriter<'a>(&'a mut DocumentHolder); @@ -76,293 +74,128 @@ impl<'a> PreferencesWriter<'a> { } } -pub struct BookmarksWriter<'a>(&'a mut DocumentHolder); - -impl<'a> BookmarksWriter<'a> { - pub(super) fn new(bookmarks: &'a mut DocumentHolder) -> Self { - Self(bookmarks) - } - - fn with_underlying_table(&mut self, fun: impl FnOnce(&mut Bookmarks, &mut ArrayOfTables)) { - self.0.edit(|values, toml_document| { - let table = toml_document.get_or_create_array_of_tables("bookmark"); - fun(values, table) - }) - } - - fn with_bookmark_table(&mut self, index: usize, fun: impl FnOnce(&mut Bookmarks, &mut Table)) { - self.with_underlying_table(|values, array_of_tables| { - let table = array_of_tables - .get_mut(index) - .expect("invalid bookmark index"); - fun(values, table) - }) - } - - pub fn add(&mut self, bookmark: Bookmark) { - self.with_underlying_table(|values, table| { - let mut bookmark_table = Table::new(); - bookmark_table["url"] = value(bookmark.url.to_string()); - bookmark_table["name"] = value(&bookmark.name); - table.push(bookmark_table); - values.push(bookmark); - }) - } - - pub fn set_url(&mut self, index: usize, url: url::Url) { - self.with_bookmark_table(index, |values, table| { - table["url"] = value(url.as_str()); - values[index].url = url; - }) - } - - pub fn set_name(&mut self, index: usize, name: String) { - self.with_bookmark_table(index, |values, table| { - table["name"] = value(&name); - values[index].name = name; - }) - } - - pub fn remove(&mut self, index: usize) { - self.with_underlying_table(|values, table| { - table.remove(index); - values.remove(index); - }) - } -} - #[cfg(test)] mod tests { use super::*; + use crate::preferences::read::read_preferences; use fluent_templates::loader::langid; - use std::ops::Deref; - macro_rules! define_serialization_test_helpers { - ($read_method:ident, $doc_struct:ty, $writer:ident) => { - fn check_roundtrip(preferences: &DocumentHolder<$doc_struct>) { - let read_result = $read_method(&preferences.serialize()); - assert_eq!( - preferences.deref(), - read_result.values(), - "roundtrip failed: expected != actual" - ); - } + ruffle_frontend_utils::define_serialization_test_helpers!( + read_preferences, + SavedGlobalPreferences, + PreferencesWriter + ); - fn test(original: &str, fun: impl FnOnce(&mut $writer), expected: &str) { - let mut preferences = $read_method(original).result; - let mut writer = $writer::new(&mut preferences); - fun(&mut writer); - check_roundtrip(&preferences); - assert_eq!(expected, preferences.serialize()); - } - }; - } - - mod preferences { - use super::*; - use crate::preferences::read::read_preferences; - - define_serialization_test_helpers!( - read_preferences, - SavedGlobalPreferences, - PreferencesWriter + #[test] + fn set_graphics_backend() { + test( + "", + |writer| writer.set_graphics_backend(GraphicsBackend::Default), + "graphics_backend = \"default\"\n", ); - #[test] - fn set_graphics_backend() { - test( - "", - |writer| writer.set_graphics_backend(GraphicsBackend::Default), - "graphics_backend = \"default\"\n", - ); - - test( - "graphics_backend = \"fast\"", - |writer| writer.set_graphics_backend(GraphicsBackend::Vulkan), - "graphics_backend = \"vulkan\"\n", - ); - } - - #[test] - fn set_graphics_power_preference() { - test( - "", - |writer| writer.set_graphics_power_preference(PowerPreference::High), - "graphics_power_preference = \"high\"\n", - ); - - test( - "graphics_power_preference = \"fast\"", - |writer| writer.set_graphics_power_preference(PowerPreference::Low), - "graphics_power_preference = \"low\"\n", - ); - } - - #[test] - fn set_language() { - test( - "", - |writer| writer.set_language(langid!("en-US")), - "language = \"en-US\"\n", - ); - - test( - "language = \"???\"", - |writer| writer.set_language(langid!("en-Latn-US-valencia")), - "language = \"en-Latn-US-valencia\"\n", - ); - } - - #[test] - fn set_output_device() { - test( - "", - |writer| writer.set_output_device(Some("Speakers".to_string())), - "output_device = \"Speakers\"\n", - ); - - test( - "output_device = \"Speakers\"", - |writer| writer.set_output_device(None), - "", - ); - } - - #[test] - fn set_volume() { - test("", |writer| writer.set_volume(0.5), "volume = 0.5\n"); - } - - #[test] - fn set_mute() { - test("", |writer| writer.set_mute(true), "mute = true\n"); - test( - "mute = true", - |writer| writer.set_mute(false), - "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", - ); - } - - #[test] - fn set_storage_backend() { - test( - "", - |writer| writer.set_storage_backend(StorageBackend::Disk), - "storage = { backend = \"disk\" }\n", - ); - test( - "storage = { backend = \"disk\" }\n", - |writer| writer.set_storage_backend(StorageBackend::Memory), - "storage = { backend = \"memory\" }\n", - ); - test( - "[storage]\nbackend = \"disk\"\n", - |writer| writer.set_storage_backend(StorageBackend::Memory), - "[storage]\nbackend = \"memory\"\n", - ); - } + test( + "graphics_backend = \"fast\"", + |writer| writer.set_graphics_backend(GraphicsBackend::Vulkan), + "graphics_backend = \"vulkan\"\n", + ); } - #[allow(clippy::unwrap_used)] - mod bookmarks { - use super::*; - use ruffle_frontend_utils::bookmarks::read_bookmarks; - use std::str::FromStr; - use url::Url; - - define_serialization_test_helpers!(read_bookmarks, Bookmarks, BookmarksWriter); - - #[test] - fn add_bookmark() { - test( - "", - |writer| { - writer.add(Bookmark { - url: Url::from_str("file:///home/user/example.swf").unwrap(), - name: "example.swf".to_string(), - }) - }, - "[[bookmark]]\nurl = \"file:///home/user/example.swf\"\nname = \"example.swf\"\n", - ); - test("[[bookmark]]\nurl = \"file:///home/user/example.swf\"\n", |writer| writer.add(Bookmark { - url: Url::from_str("file:///home/user/another_file.swf").unwrap(), - name: "another_file.swf".to_string(), - }), "[[bookmark]]\nurl = \"file:///home/user/example.swf\"\n\n[[bookmark]]\nurl = \"file:///home/user/another_file.swf\"\nname = \"another_file.swf\"\n"); - } - - #[test] - fn modify_bookmark() { - test( - "[[bookmark]]\nurl = \"file:///example.swf\"\n", - |writer| writer.set_name(0, "Custom Name".to_string()), - "[[bookmark]]\nurl = \"file:///example.swf\"\nname = \"Custom Name\"\n", - ); - test( - "[[bookmark]]\nurl = \"file:///example.swf\"\nname = \"example.swf\"", - |writer| writer.set_url(0, Url::parse("https://ruffle.rs/logo-anim.swf").unwrap()), - "[[bookmark]]\nurl = \"https://ruffle.rs/logo-anim.swf\"\nname = \"example.swf\"\n", - ); - } - - #[test] - fn remove_bookmark() { - test( - "[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\nurl = \"https://ruffle.rs/logo-anim.swf\"\n\n[[bookmark]]\nurl = \"file:///another_file.swf\"\n", - |writer| { - writer.remove(1); - }, - "[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\nurl = \"file:///another_file.swf\"\n", + #[test] + fn set_graphics_power_preference() { + test( + "", + |writer| writer.set_graphics_power_preference(PowerPreference::High), + "graphics_power_preference = \"high\"\n", ); - test("[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\n\n[[bookmark]]\nurl = \"https://ruffle.rs/logo-anim.swf\"\n\n[[bookmark]]\nurl = \"invalid\"\n", |writer| { - writer.remove(2); - }, "[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\n\n[[bookmark]]\nurl = \"invalid\"\n"); - // check if we can remove invalid entries. - test("[[bookmark]]", |writer| writer.remove(0), ""); - } + test( + "graphics_power_preference = \"fast\"", + |writer| writer.set_graphics_power_preference(PowerPreference::Low), + "graphics_power_preference = \"low\"\n", + ); + } - #[test] - fn overwrite_invalid_bookmark_type() { - test( - "[bookmark]", - |writer| { - writer.add(Bookmark { - url: Url::from_str("file:///test.swf").unwrap(), - name: "test.swf".to_string(), - }) - }, - "[[bookmark]]\nurl = \"file:///test.swf\"\nname = \"test.swf\"\n", - ); + #[test] + fn set_language() { + test( + "", + |writer| writer.set_language(langid!("en-US")), + "language = \"en-US\"\n", + ); - test( - "bookmark = 1010", - |writer| { - writer.add(Bookmark { - url: Url::from_str("file:///test.swf").unwrap(), - name: "test.swf".to_string(), - }) - }, - "[[bookmark]]\nurl = \"file:///test.swf\"\nname = \"test.swf\"\n", - ); - } + test( + "language = \"???\"", + |writer| writer.set_language(langid!("en-Latn-US-valencia")), + "language = \"en-Latn-US-valencia\"\n", + ); + } + + #[test] + fn set_output_device() { + test( + "", + |writer| writer.set_output_device(Some("Speakers".to_string())), + "output_device = \"Speakers\"\n", + ); + + test( + "output_device = \"Speakers\"", + |writer| writer.set_output_device(None), + "", + ); + } + + #[test] + fn set_volume() { + test("", |writer| writer.set_volume(0.5), "volume = 0.5\n"); + } + + #[test] + fn set_mute() { + test("", |writer| writer.set_mute(true), "mute = true\n"); + test( + "mute = true", + |writer| writer.set_mute(false), + "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", + ); + } + + #[test] + fn set_storage_backend() { + test( + "", + |writer| writer.set_storage_backend(StorageBackend::Disk), + "storage = { backend = \"disk\" }\n", + ); + test( + "storage = { backend = \"disk\" }\n", + |writer| writer.set_storage_backend(StorageBackend::Memory), + "storage = { backend = \"memory\" }\n", + ); + test( + "[storage]\nbackend = \"disk\"\n", + |writer| writer.set_storage_backend(StorageBackend::Memory), + "[storage]\nbackend = \"memory\"\n", + ); } } diff --git a/frontend-utils/src/bookmarks.rs b/frontend-utils/src/bookmarks.rs index a82b30fc2..3fd2cb28b 100644 --- a/frontend-utils/src/bookmarks.rs +++ b/frontend-utils/src/bookmarks.rs @@ -1,5 +1,7 @@ mod read; +mod write; pub use read::read_bookmarks; +pub use write::BookmarksWriter; use url::Url; diff --git a/frontend-utils/src/bookmarks/write.rs b/frontend-utils/src/bookmarks/write.rs new file mode 100644 index 000000000..6b2048c60 --- /dev/null +++ b/frontend-utils/src/bookmarks/write.rs @@ -0,0 +1,142 @@ +use crate::bookmarks::{Bookmark, Bookmarks}; +use crate::parse::DocumentHolder; +use crate::write::TableExt; +use toml_edit::{value, ArrayOfTables, Table}; + +pub struct BookmarksWriter<'a>(&'a mut DocumentHolder); + +impl<'a> BookmarksWriter<'a> { + pub fn new(bookmarks: &'a mut DocumentHolder) -> Self { + Self(bookmarks) + } + + fn with_underlying_table(&mut self, fun: impl FnOnce(&mut Bookmarks, &mut ArrayOfTables)) { + self.0.edit(|values, toml_document| { + let table = toml_document.get_or_create_array_of_tables("bookmark"); + fun(values, table) + }) + } + + fn with_bookmark_table(&mut self, index: usize, fun: impl FnOnce(&mut Bookmarks, &mut Table)) { + self.with_underlying_table(|values, array_of_tables| { + let table = array_of_tables + .get_mut(index) + .expect("invalid bookmark index"); + fun(values, table) + }) + } + + pub fn add(&mut self, bookmark: Bookmark) { + self.with_underlying_table(|values, table| { + let mut bookmark_table = Table::new(); + bookmark_table["url"] = value(bookmark.url.to_string()); + bookmark_table["name"] = value(&bookmark.name); + table.push(bookmark_table); + values.push(bookmark); + }) + } + + pub fn set_url(&mut self, index: usize, url: url::Url) { + self.with_bookmark_table(index, |values, table| { + table["url"] = value(url.as_str()); + values[index].url = url; + }) + } + + pub fn set_name(&mut self, index: usize, name: String) { + self.with_bookmark_table(index, |values, table| { + table["name"] = value(&name); + values[index].name = name; + }) + } + + pub fn remove(&mut self, index: usize) { + self.with_underlying_table(|values, table| { + table.remove(index); + values.remove(index); + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::bookmarks::read_bookmarks; + use url::Url; + + crate::define_serialization_test_helpers!(read_bookmarks, Bookmarks, BookmarksWriter); + + #[test] + fn add_bookmark() { + test( + "", + |writer| { + writer.add(Bookmark { + url: Url::parse("file:///home/user/example.swf").unwrap(), + name: "example.swf".to_string(), + }) + }, + "[[bookmark]]\nurl = \"file:///home/user/example.swf\"\nname = \"example.swf\"\n", + ); + test("[[bookmark]]\nurl = \"file:///home/user/example.swf\"\n", |writer| writer.add(Bookmark { + url: Url::parse("file:///home/user/another_file.swf").unwrap(), + name: "another_file.swf".to_string(), + }), "[[bookmark]]\nurl = \"file:///home/user/example.swf\"\n\n[[bookmark]]\nurl = \"file:///home/user/another_file.swf\"\nname = \"another_file.swf\"\n"); + } + + #[test] + fn modify_bookmark() { + test( + "[[bookmark]]\nurl = \"file:///example.swf\"\n", + |writer| writer.set_name(0, "Custom Name".to_string()), + "[[bookmark]]\nurl = \"file:///example.swf\"\nname = \"Custom Name\"\n", + ); + test( + "[[bookmark]]\nurl = \"file:///example.swf\"\nname = \"example.swf\"", + |writer| writer.set_url(0, Url::parse("https://ruffle.rs/logo-anim.swf").unwrap()), + "[[bookmark]]\nurl = \"https://ruffle.rs/logo-anim.swf\"\nname = \"example.swf\"\n", + ); + } + + #[test] + fn remove_bookmark() { + test( + "[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\nurl = \"https://ruffle.rs/logo-anim.swf\"\n\n[[bookmark]]\nurl = \"file:///another_file.swf\"\n", + |writer| { + writer.remove(1); + }, + "[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\nurl = \"file:///another_file.swf\"\n", + ); + test("[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\n\n[[bookmark]]\nurl = \"https://ruffle.rs/logo-anim.swf\"\n\n[[bookmark]]\nurl = \"invalid\"\n", |writer| { + writer.remove(2); + }, "[[bookmark]]\nurl = \"file://home/user/example.swf\"\n\n[[bookmark]]\n\n[[bookmark]]\nurl = \"invalid\"\n"); + + // check if we can remove invalid entries. + test("[[bookmark]]", |writer| writer.remove(0), ""); + } + + #[test] + fn overwrite_invalid_bookmark_type() { + test( + "[bookmark]", + |writer| { + writer.add(Bookmark { + url: Url::parse("file:///test.swf").unwrap(), + name: "test.swf".to_string(), + }) + }, + "[[bookmark]]\nurl = \"file:///test.swf\"\nname = \"test.swf\"\n", + ); + + test( + "bookmark = 1010", + |writer| { + writer.add(Bookmark { + url: Url::parse("file:///test.swf").unwrap(), + name: "test.swf".to_string(), + }) + }, + "[[bookmark]]\nurl = \"file:///test.swf\"\nname = \"test.swf\"\n", + ); + } +} diff --git a/frontend-utils/src/write.rs b/frontend-utils/src/write.rs index f0aea2e5b..4857dc3c8 100644 --- a/frontend-utils/src/write.rs +++ b/frontend-utils/src/write.rs @@ -20,3 +20,26 @@ impl TableExt for Table { .expect("type was just created") } } + +#[macro_export] +macro_rules! define_serialization_test_helpers { + ($read_method:ident, $doc_struct:ty, $writer:ident) => { + fn check_roundtrip(preferences: &DocumentHolder<$doc_struct>) { + use std::ops::Deref; + let read_result = $read_method(&preferences.serialize()); + assert_eq!( + preferences.deref(), + read_result.values(), + "roundtrip failed: expected != actual" + ); + } + + fn test(original: &str, fun: impl FnOnce(&mut $writer), expected: &str) { + let mut preferences = $read_method(original).result; + let mut writer = $writer::new(&mut preferences); + fun(&mut writer); + check_roundtrip(&preferences); + assert_eq!(expected, preferences.serialize()); + } + }; +}