Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

settings diff and saving #165

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions crates/bevy_editor_settings/src/diff/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
mod opaque;
mod structs;

use bevy::{
prelude::warn,
reflect::{PartialReflect, Reflect, ReflectRef, Struct, TypeInfo},
utils::HashMap,
};
use opaque::DiffOpaque;
use structs::DiffStructs;

#[derive(Debug, Clone)]
pub(crate) enum DiffType {
Opaque,
Struct(HashMap<String, DiffType>),
}

pub trait Diff {
type Input;

fn diff(&self, input1: Self::Input, input2: Self::Input) -> Option<DiffType>;
}

pub struct DiffStructures<'a> {
pub type_info: &'a TypeInfo,
}

impl<'a> Diff for DiffStructures<'a> {
type Input = &'a dyn PartialReflect;

fn diff(&self, input1: Self::Input, input2: Self::Input) -> Option<DiffType> {
match self.type_info {
TypeInfo::Struct(struct_info) => match (input1.reflect_ref(), input2.reflect_ref()) {
(ReflectRef::Struct(struct1), ReflectRef::Struct(struct2)) => {
DiffStructs { struct_info }.diff(struct1, struct2)
}
_ => {
warn!("Diffing not implemented for type: {:?}", self.type_info);
None
}
},
TypeInfo::Opaque(opaque_info) => DiffOpaque { opaque_info }.diff(input1, input2),
_ => {
warn!("Diffing not implemented for type: {:?}", self.type_info);
None
}
}
}
}

#[derive(Debug, Clone, PartialEq)]
struct DiffResult {
field_name: String,
old_value: String,
new_value: String,
}

#[derive(Debug, Clone, Reflect)]
struct SomeStruct {
a: u32,
b: u32,
}

#[derive(Debug, Clone, Reflect)]
struct Wrapper {
struct1: SomeStruct,
something: u32,
}

#[cfg(test)]
mod tests {
use bevy::reflect::DynamicTyped as _;

use super::*;

#[test]
fn test_diff() {
let struct1 = SomeStruct { a: 1, b: 2 };
let struct2 = SomeStruct { a: 1, b: 3 };

let wrapper1 = Wrapper {
struct1,
something: 1,
};

let wrapper2 = Wrapper {
struct1: struct2,
something: 1,
};

let diff = DiffStructures {
type_info: wrapper1.reflect_type_info(),
}
.diff(&wrapper1, &wrapper2)
.unwrap();

println!("{:?}", diff);

// assert_eq!(
// diff,
// vec![DiffResult {
// field_name: "b".to_string(),
// old_value: "2".to_string(),
// new_value: "3".to_string(),
// }]
// );
}
}
68 changes: 68 additions & 0 deletions crates/bevy_editor_settings/src/diff/opaque.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use bevy::reflect::{OpaqueInfo, PartialReflect};

use super::{Diff, DiffResult, DiffType};

macro_rules! diff_types {
($type_info:expr, $input1:expr, $input2:expr, $($type_v:ty),*) => {
$(
if diff_type::<$type_v>(&$type_info, $input1, $input2){
return Some(DiffType::Opaque);
};
)*
};

}

pub struct DiffOpaque<'a> {
pub opaque_info: &'a OpaqueInfo,
}

impl<'a> Diff for DiffOpaque<'a> {
type Input = &'a dyn PartialReflect;

fn diff(&self, input1: Self::Input, input2: Self::Input) -> Option<DiffType> {
diff_types!(
self.opaque_info,
input1,
input2,
bool,
u8,
u16,
u32,
u64,
i32,
i64,
f32,
f64,
String,
&str
);

None
}
}

#[inline]
fn diff_type<T>(
opaque_info: &OpaqueInfo,
input1: &dyn PartialReflect,
input2: &dyn PartialReflect,
) -> bool
where
T: PartialEq + 'static,
{
if opaque_info.is::<T>() {
let value1 = input1.try_downcast_ref::<T>();
let value2 = input2.try_downcast_ref::<T>();

if value1.is_none() || value2.is_none() {
return false;
}
let value1 = value1.unwrap();
let value2 = value2.unwrap();
if value1 != value2 {
return true;
}
}
false
}
32 changes: 32 additions & 0 deletions crates/bevy_editor_settings/src/diff/structs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use bevy::{reflect::Struct, utils::hashbrown::HashMap};

use crate::utils::struct_utils::StructLikeInfo;

use super::{Diff, DiffResult, DiffType};

pub struct DiffStructs<'a> {
pub struct_info: &'a dyn StructLikeInfo,
}

impl<'a> Diff for DiffStructs<'a> {
type Input = &'a dyn Struct;

fn diff(&self, input1: Self::Input, input2: Self::Input) -> Option<DiffType> {
let mut results = HashMap::new();

for index in 0..self.struct_info.field_len() {
let field = input1.field_at(index).unwrap();
let field_other = input2.field_at(index).unwrap();
let type_info = field.get_represented_type_info().unwrap();
if let Some(output) = (super::DiffStructures { type_info }).diff(field, field_other) {
results.insert(self.struct_info.field_at(index).unwrap().name().to_string(), output);
}
}

if results.is_empty() {
None
} else {
Some(DiffType::Struct(results))
}
}
}
21 changes: 10 additions & 11 deletions crates/bevy_editor_settings/src/file_system/de/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,39 @@ use bevy::{
reflect::{Array, ArrayInfo},
};

use super::LoadStructure;
use super::{LoadStructure, StructureLoader};

pub struct LoadArray<'a> {
pub array_info: &'a ArrayInfo,
pub array: &'a mut dyn Array,
pub toml_array: &'a toml::value::Array,
}

impl LoadArray<'_> {
pub fn load_array(self) {
if self.toml_array.len() != self.array_info.capacity() {
impl<'a> StructureLoader for LoadArray<'a> {
type Input = &'a toml::value::Array;

fn load(self, input: Self::Input) {
if input.len() != self.array_info.capacity() {
warn!(
"Preferences: Expected Array length {}, got {}",
self.array_info.capacity(),
self.toml_array.len()
input.len()
);
return;
}

for i in 0..self.array_info.capacity() {
let Some(toml_value) = self.toml_array.get(i) else {
let Some(toml_value) = input.get(i) else {
continue;
};

let field_mut = self.array.get_mut(i).unwrap();

LoadStructure {
type_info: field_mut.get_represented_type_info().unwrap(),
table: toml_value,
structure: field_mut,
custom_attributes: None,
}
.load();
.load(toml_value);
}
}
}
Expand All @@ -54,10 +54,9 @@ mod tests {
let toml_value = toml::Value::Array(vec![toml::Value::Integer(1), toml::Value::Integer(2)]);
LoadArray {
array_info: array.reflect_type_info().as_array().unwrap(),
toml_array: toml_value.as_array().unwrap(),
array: &mut array,
}
.load_array();
.load(toml_value.as_array().unwrap());
assert_eq!(array, [1, 2]);
}
}
3 changes: 2 additions & 1 deletion crates/bevy_editor_settings/src/file_system/de/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use bevy::reflect::{
EnumInfo, ListInfo, MapInfo, PartialReflect, SetInfo, Type, TypeInfo,
};

use super::{struct_utils::StructLikeInfo, tuple_utils::TupleLikeInfo};
use crate::utils::{struct_utils::StructLikeInfo, tuple_utils::TupleLikeInfo};


pub fn default_data_type(type_info: &TypeInfo) -> Option<Box<dyn PartialReflect>> {
match type_info {
Expand Down
28 changes: 12 additions & 16 deletions crates/bevy_editor_settings/src/file_system/de/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ use bevy::{
reflect::{DynamicEnum, DynamicVariant, Enum, EnumInfo, VariantInfo},
};

use super::{structs::LoadStruct, tuple::LoadTuple};
use super::{structs::LoadStruct, tuple::LoadTuple, StructureLoader};

pub struct LoadEnum<'a> {
pub enum_info: &'a EnumInfo,
pub toml_value: &'a toml::Value,
pub enm: &'a mut dyn Enum,
}

impl LoadEnum<'_> {
pub fn load_enum(self) {
match self.toml_value {
impl<'a> StructureLoader for LoadEnum<'a> {
type Input = &'a toml::Value;

fn load(self, input: Self::Input) {
match input {
toml::Value::String(str_val) => {
if let Some(VariantInfo::Unit(variant)) = self.enum_info.variant(str_val) {
let dyn_enum = DynamicEnum::new(variant.name(), DynamicVariant::Unit);
Expand Down Expand Up @@ -51,10 +52,9 @@ impl LoadEnum<'_> {

LoadStruct {
struct_info,
table: map,
strct: &mut dyn_struct,
}
.load_struct();
.load(map);

let dyn_enum = DynamicEnum::new(
variant_info.name(),
Expand Down Expand Up @@ -82,10 +82,9 @@ impl LoadEnum<'_> {

LoadTuple {
tuple_info: tuple_variant_info,
table: array,
tuple: &mut dyn_tuple,
}
.load_tuple();
.load(array);

let dyn_enum = DynamicEnum::new(
variant_info.name(),
Expand All @@ -97,7 +96,7 @@ impl LoadEnum<'_> {
}
}
_ => {
warn!("Preferences: Unsupported type: {:?}", self.toml_value);
warn!("Preferences: Unsupported type: {:?}", input);
}
}
}
Expand Down Expand Up @@ -128,10 +127,9 @@ mod tests {
let toml_value = toml::Value::String("Variant1".to_string());
LoadEnum {
enum_info: enum_test.reflect_type_info().as_enum().unwrap(),
toml_value: &toml_value,
enm: &mut enum_test,
}
.load_enum();
.load(&toml_value);

assert_eq!(enum_test, TestEnum::Variant1);
}
Expand All @@ -153,10 +151,9 @@ mod tests {
let toml_value = enum_test_toml();
LoadEnum {
enum_info: enum_test.reflect_type_info().as_enum().unwrap(),
toml_value: &toml_value,
enm: &mut enum_test,
}
.load_enum();
.load(&toml_value);

assert_eq!(
enum_test,
Expand Down Expand Up @@ -184,10 +181,9 @@ mod tests {
let toml_value = enum_test_tuple_toml();
LoadEnum {
enum_info: enum_test.reflect_type_info().as_enum().unwrap(),
toml_value: &toml_value,
enm: &mut enum_test,
}
.load_enum();
.load(&toml_value);

assert_eq!(enum_test, TestEnum::Variant4(1, 2));
}
Expand Down
Loading
Loading