frontend-utils: Add new package and moved custom toml parsing utilities there

This commit is contained in:
Nathan Adams 2024-04-04 00:16:03 +02:00
parent 53f49eb480
commit 5ed6115dcc
7 changed files with 218 additions and 189 deletions

8
Cargo.lock generated
View File

@ -4259,6 +4259,7 @@ dependencies = [
"os_info",
"rfd",
"ruffle_core",
"ruffle_frontend_utils",
"ruffle_render",
"ruffle_render_wgpu",
"ruffle_video_software",
@ -4280,6 +4281,13 @@ dependencies = [
"winit",
]
[[package]]
name = "ruffle_frontend_utils"
version = "0.1.0"
dependencies = [
"toml_edit 0.22.9",
]
[[package]]
name = "ruffle_gc_arena"
version = "0.0.0"

View File

@ -12,6 +12,8 @@ members = [
"scanner",
"exporter",
"frontend-utils",
"render",
"render/canvas",
"render/naga-agal",

View File

@ -23,6 +23,7 @@ ruffle_core = { path = "../core", features = ["audio", "clap", "mp3", "nellymose
ruffle_render = { path = "../render", features = ["clap"] }
ruffle_render_wgpu = { path = "../render/wgpu", features = ["clap"] }
ruffle_video_software = { path = "../video/software", optional = true }
ruffle_frontend_utils = { path = "../frontend-utils" }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
tracing-appender = "0.2.3"

View File

@ -1,194 +1,7 @@
use crate::preferences::{Bookmark, SavedGlobalPreferences};
use std::fmt;
use ruffle_frontend_utils::parse::{ParseContext, ParseResult, ReadExt};
use std::str::FromStr;
use toml_edit::{ArrayOfTables, DocumentMut, Item, Table, TableLike};
#[derive(Debug, PartialEq)]
pub struct ParseResult<T: PartialEq + fmt::Debug> {
pub result: T,
pub warnings: Vec<String>,
}
impl<T: fmt::Debug + PartialEq> ParseResult<T> {
fn add_warning(&mut self, message: String) {
self.warnings.push(message);
}
}
#[derive(Default)]
pub struct ParseContext {
warnings: Vec<String>,
/// Path of the current item being parsed
path: Vec<&'static str>,
}
impl ParseContext {
pub fn push_key(&mut self, key: &'static str) {
self.path.push(key);
}
pub fn pop_key(&mut self) {
let _ = self.path.pop();
}
pub fn path(&self) -> String {
self.path.join(".")
}
pub fn add_warning(&mut self, warning: String) {
self.warnings.push(warning);
}
}
pub trait ReadExt<'a> {
fn get_impl(&'a self, key: &str) -> Option<&'a Item>;
fn get_table_like(
&'a self,
cx: &mut ParseContext,
key: &'static str,
fun: impl FnOnce(&mut ParseContext, &dyn TableLike),
) {
if let Some(item) = self.get_impl(key) {
cx.push_key(key);
if let Some(table) = item.as_table_like() {
fun(cx, table);
} else {
cx.add_warning(format!(
"Invalid {}: expected table but found {}",
cx.path(),
item.type_name()
));
}
cx.pop_key();
}
}
fn get_array_of_tables(
&'a self,
cx: &mut ParseContext,
key: &'static str,
fun: impl FnOnce(&mut ParseContext, &ArrayOfTables),
) {
if let Some(item) = self.get_impl(key) {
cx.push_key(key);
if let Some(array) = item.as_array_of_tables() {
fun(cx, array);
} else {
cx.add_warning(format!(
"Invalid {}: expected array of tables but found {}",
cx.path(),
item.type_name()
));
}
cx.pop_key();
}
}
fn parse_from_str<T: FromStr>(&'a self, cx: &mut ParseContext, key: &'static str) -> Option<T> {
cx.push_key(key);
let res = if let Some(item) = self.get_impl(key) {
if let Some(str) = item.as_str() {
if let Ok(value) = str.parse::<T>() {
Some(value)
} else {
cx.add_warning(format!("Invalid {}: unsupported value {str:?}", cx.path()));
None
}
} else {
cx.add_warning(format!(
"Invalid {}: expected string but found {}",
cx.path(),
item.type_name()
));
None
}
} else {
None
};
cx.pop_key();
res
}
fn get_bool(&'a self, cx: &mut ParseContext, key: &'static str) -> Option<bool> {
cx.push_key(key);
let res = if let Some(item) = self.get_impl(key) {
if let Some(value) = item.as_bool() {
Some(value)
} else {
cx.add_warning(format!(
"Invalid {}: expected boolean but found {}",
cx.path(),
item.type_name()
));
None
}
} else {
None
};
cx.pop_key();
res
}
fn get_float(&'a self, cx: &mut ParseContext, key: &'static str) -> Option<f64> {
cx.push_key(key);
let res = if let Some(item) = self.get_impl(key) {
if let Some(value) = item.as_float() {
Some(value)
} else {
cx.add_warning(format!(
"Invalid {}: expected float but found {}",
cx.path(),
item.type_name()
));
None
}
} else {
None
};
cx.pop_key();
res
}
}
// Implementations for toml_edit types.
impl<'a> ReadExt<'a> for DocumentMut {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}
impl<'a> ReadExt<'a> for Item {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}
impl<'a> ReadExt<'a> for Table {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}
impl<'a> ReadExt<'a> for dyn TableLike + 'a {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}
use toml_edit::DocumentMut;
/// Read the given preferences into a **guaranteed valid** `SavedGlobalPreferences`,
/// recording any possible warnings encountered along the way.

14
frontend-utils/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "ruffle_frontend_utils"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true
[lints]
workspace = true
[dependencies]
toml_edit = { version = "0.22.9", features = ["parse"] }

View File

@ -0,0 +1 @@
pub mod parse;

190
frontend-utils/src/parse.rs Normal file
View File

@ -0,0 +1,190 @@
use std::fmt;
use std::str::FromStr;
use toml_edit::{ArrayOfTables, DocumentMut, Item, Table, TableLike};
#[derive(Debug, PartialEq)]
pub struct ParseResult<T: PartialEq + fmt::Debug> {
pub result: T,
pub warnings: Vec<String>,
}
impl<T: fmt::Debug + PartialEq> ParseResult<T> {
pub fn add_warning(&mut self, message: String) {
self.warnings.push(message);
}
}
#[derive(Default)]
pub struct ParseContext {
pub warnings: Vec<String>,
/// Path of the current item being parsed
pub path: Vec<&'static str>,
}
impl ParseContext {
pub fn push_key(&mut self, key: &'static str) {
self.path.push(key);
}
pub fn pop_key(&mut self) {
let _ = self.path.pop();
}
pub fn path(&self) -> String {
self.path.join(".")
}
pub fn add_warning(&mut self, warning: String) {
self.warnings.push(warning);
}
}
pub trait ReadExt<'a> {
fn get_impl(&'a self, key: &str) -> Option<&'a Item>;
fn get_table_like(
&'a self,
cx: &mut ParseContext,
key: &'static str,
fun: impl FnOnce(&mut ParseContext, &dyn TableLike),
) {
if let Some(item) = self.get_impl(key) {
cx.push_key(key);
if let Some(table) = item.as_table_like() {
fun(cx, table);
} else {
cx.add_warning(format!(
"Invalid {}: expected table but found {}",
cx.path(),
item.type_name()
));
}
cx.pop_key();
}
}
fn get_array_of_tables(
&'a self,
cx: &mut ParseContext,
key: &'static str,
fun: impl FnOnce(&mut ParseContext, &ArrayOfTables),
) {
if let Some(item) = self.get_impl(key) {
cx.push_key(key);
if let Some(array) = item.as_array_of_tables() {
fun(cx, array);
} else {
cx.add_warning(format!(
"Invalid {}: expected array of tables but found {}",
cx.path(),
item.type_name()
));
}
cx.pop_key();
}
}
fn parse_from_str<T: FromStr>(&'a self, cx: &mut ParseContext, key: &'static str) -> Option<T> {
cx.push_key(key);
let res = if let Some(item) = self.get_impl(key) {
if let Some(str) = item.as_str() {
if let Ok(value) = str.parse::<T>() {
Some(value)
} else {
cx.add_warning(format!("Invalid {}: unsupported value {str:?}", cx.path()));
None
}
} else {
cx.add_warning(format!(
"Invalid {}: expected string but found {}",
cx.path(),
item.type_name()
));
None
}
} else {
None
};
cx.pop_key();
res
}
fn get_bool(&'a self, cx: &mut ParseContext, key: &'static str) -> Option<bool> {
cx.push_key(key);
let res = if let Some(item) = self.get_impl(key) {
if let Some(value) = item.as_bool() {
Some(value)
} else {
cx.add_warning(format!(
"Invalid {}: expected boolean but found {}",
cx.path(),
item.type_name()
));
None
}
} else {
None
};
cx.pop_key();
res
}
fn get_float(&'a self, cx: &mut ParseContext, key: &'static str) -> Option<f64> {
cx.push_key(key);
let res = if let Some(item) = self.get_impl(key) {
if let Some(value) = item.as_float() {
Some(value)
} else {
cx.add_warning(format!(
"Invalid {}: expected float but found {}",
cx.path(),
item.type_name()
));
None
}
} else {
None
};
cx.pop_key();
res
}
}
// Implementations for toml_edit types.
impl<'a> ReadExt<'a> for DocumentMut {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}
impl<'a> ReadExt<'a> for Item {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}
impl<'a> ReadExt<'a> for Table {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}
impl<'a> ReadExt<'a> for dyn TableLike + 'a {
fn get_impl(&'a self, key: &str) -> Option<&'a Item> {
self.get(key)
}
}