frontend-utils: Add new package and moved custom toml parsing utilities there
This commit is contained in:
parent
53f49eb480
commit
5ed6115dcc
|
@ -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"
|
||||
|
|
|
@ -12,6 +12,8 @@ members = [
|
|||
"scanner",
|
||||
"exporter",
|
||||
|
||||
"frontend-utils",
|
||||
|
||||
"render",
|
||||
"render/canvas",
|
||||
"render/naga-agal",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"] }
|
|
@ -0,0 +1 @@
|
|||
pub mod parse;
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue