From 5d0317b145b680809006444dbb25e8cef8c113ed Mon Sep 17 00:00:00 2001 From: PhaestusFox Date: Tue, 1 Jun 2021 19:15:23 +0800 Subject: [PATCH 1/2] added basic serialization added a feature to the create called "serialize" that adds the ability to save and load the actions of an input map to a file. also made an example of how one might use said feature and a bunch of tests to make sure what is save and what is loaded is the same. could maybe make it auto load files if the exist but this would need some thought about how to expose the save path to the developer. could also add a version/merge system so that if an old keybind file is loaded new key binds will be added automatically; didn't do the auto merging here because it would mean working out a way to not override the custom keybinds and also not conflict with them --- Cargo.toml | 5 ++ examples/hello_world_serde.rs | 65 +++++++++++++++++++++ src/lib.rs | 8 +++ src/serialize.rs | 103 ++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 examples/hello_world_serde.rs create mode 100644 src/serialize.rs diff --git a/Cargo.toml b/Cargo.toml index e9636b8..f06682e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,11 @@ edition = "2018" [dependencies] bevy = { version = "0.5", default-features = false } +ron = { version = "*", optional = true } +serde = { version = "*", optional = true } [dev-dependencies] bevy = { version = "0.5", default-features = false, features = ["bevy_winit", "x11"] } + +[features] +serialize = ["ron", "bevy/serialize", "serde"] \ No newline at end of file diff --git a/examples/hello_world_serde.rs b/examples/hello_world_serde.rs new file mode 100644 index 0000000..2316f98 --- /dev/null +++ b/examples/hello_world_serde.rs @@ -0,0 +1,65 @@ +use bevy::prelude::*; +use bevy_input_actionmap::*; +fn main() { + App::build() + .add_plugins(DefaultPlugins) + .add_plugin(ActionPlugin::::default()) + .add_startup_system(setup.system()) + .add_system(run_commands.system()) + .run(); +} + +#[derive(Hash, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +enum Action { + Select, + SuperSelect, + AwesomeSuperSelect, +} + +#[cfg(feature = "serialize")] +fn setup(mut input: ResMut>) { + if let Err(_) = input.load_from_path("keybinds.config") { + { println!("no keybind config found creating default setup"); //just to show the path it took + input + .bind(Action::Select, KeyCode::Return) + .bind(Action::Select, GamepadButtonType::South) + .bind(Action::SuperSelect, vec![KeyCode::LAlt, KeyCode::Return]) + .bind(Action::SuperSelect, vec![KeyCode::RAlt, KeyCode::Return]) + // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. + .bind( + Action::AwesomeSuperSelect, + vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], + ); + input.save_to_path("keybinds.config").unwrap()} + } else {//if it loaded custom keybinds dont add new ones + println!("keybinds loaded from local file") //just to show the path it took + } + +} +#[cfg(not(feature = "serialize"))] +fn setup(mut input: ResMut>){ + println!("serialize feature is off so this is the same as hello_world_enum; Why just Why"); + input + .bind(Action::Select, KeyCode::Return) + .bind(Action::Select, GamepadButtonType::South) + .bind(Action::SuperSelect, vec![KeyCode::LAlt, KeyCode::Return]) + .bind(Action::SuperSelect, vec![KeyCode::RAlt, KeyCode::Return]) + // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. + .bind( + Action::AwesomeSuperSelect, + vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], + ); +} + +fn run_commands(input: Res>) { + if input.just_active(Action::Select) { + println!("Selected"); + } + if input.just_active(Action::SuperSelect) { + println!("Super selected"); + } + if input.just_active(Action::AwesomeSuperSelect) { + println!("Awesome super selected"); + } +} diff --git a/src/lib.rs b/src/lib.rs index eb3b807..b5eca1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,13 @@ use bevy::{ prelude::*, }; +#[cfg(feature = "serialize")] +pub mod serialize; +#[cfg(feature = "serialize")] +pub use serde::{Serialize, Deserialize}; + #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Binding { keys: HashSet, gamepad_buttons: HashSet, @@ -66,6 +72,7 @@ impl From> for Binding { } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub enum GamepadAxisDirection { LeftStickXPositive, LeftStickXNegative, @@ -130,6 +137,7 @@ impl Binding { } #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Action { bindings: Vec, } diff --git a/src/serialize.rs b/src/serialize.rs new file mode 100644 index 0000000..7953dbc --- /dev/null +++ b/src/serialize.rs @@ -0,0 +1,103 @@ +use super::*; +use serde::{Serialize, de::DeserializeOwned}; + +#[cfg(test)] +mod test{ + const ACTION_SELECT: &str = "SELECT"; + const ACTION_SUPER_SELECT: &str = "SUPER_SELECT"; + const ACTION_AWESOME_SUPER_SELECT: &str = "AWESOME_SUPER_SELECT"; + const TESTPATH : &str = "test.keymap"; + use bevy::prelude::*; + use super::*; + #[derive(Hash, PartialEq, Eq, Clone, Serialize, serde::Deserialize, Debug)] + enum TestAction { + Select, + SuperSelect, + AwesomeSuperSelect, + } + fn test_binding(to_test : T) where T : Into{ + let binding : Binding = to_test.into(); + let serialized = ron::to_string(&binding).expect("Failed to serialize binding"); + let deserialized : Binding = ron::from_str(&serialized).expect("Failed to deserialize binding"); + assert_eq!(binding, deserialized); + } + + + fn test_action(action : Action){ + let serialized = ron::to_string(&action).expect("Failed to serialize action"); + let deserialized : Action = ron::from_str(&serialized).expect("Failed to deserialize action"); + assert_eq!(action, deserialized) + } + #[test] + fn test_inputmap_string(){ + let mut map = super::InputMap::::default(); + map.bind(ACTION_SELECT, KeyCode::Return) + .bind(ACTION_SELECT, GamepadButtonType::South) + .bind(ACTION_SUPER_SELECT, vec![KeyCode::LAlt, KeyCode::Return]) + .bind(ACTION_SUPER_SELECT, vec![KeyCode::RAlt, KeyCode::Return]) + // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. + .bind( + ACTION_AWESOME_SUPER_SELECT, + vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], + ); + map.save_to_path(TESTPATH).expect("Failed to save string InputMap"); + let new_map = InputMap::::new_from_path(TESTPATH).expect("Failed to load string InputMap"); + assert_eq!(map.actions, new_map.actions); + map.load_from_path(TESTPATH).expect("Failed to Load from path"); + assert_eq!(map.actions, new_map.actions); + } + + fn test_inputmap_enum(){ + let mut map = super::InputMap::::default(); + map.bind(TestAction::Select, KeyCode::Return) + .bind(TestAction::Select, GamepadButtonType::South) + .bind(TestAction::SuperSelect, vec![KeyCode::LAlt, KeyCode::Return]) + .bind(TestAction::SuperSelect, vec![KeyCode::RAlt, KeyCode::Return]) + // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. + .bind( + TestAction::AwesomeSuperSelect, + vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], + ); + map.save_to_path(TESTPATH).expect("Failed to save enum InputMap"); + let new_map = InputMap::::new_from_path(TESTPATH).expect("Failed to load enum InputMap"); + assert_eq!(map.actions, new_map.actions); + map.load_from_path(TESTPATH).expect("Failed to Load from path"); + assert_eq!(map.actions, new_map.actions); + } + + #[test] + fn it_works(){ + test_binding(KeyCode::Space); + test_binding(vec![KeyCode::Space, KeyCode::LControl]); + test_binding(GamepadButtonType::North); + test_binding(vec![GamepadButtonType::North, GamepadButtonType::LeftThumb]); + test_action(Action{ + bindings : vec![KeyCode::Space.into(), KeyCode::J.into()] + }); + test_inputmap_string(); + test_inputmap_enum(); + std::fs::remove_file(TESTPATH).expect("Failed to remove testsave file"); + } +} + +impl InputMap { + pub fn save_to_path(&self, path : &str) -> std::io::Result<()> where T : Serialize{ + let contents = ron::ser::to_string_pretty(&self.actions, ron::ser::PrettyConfig::default()).expect("There was an error making the string"); + std::fs::write(path, contents) + } + pub fn load_from_path(&mut self, path : &str) -> std::io::Result<()> where T : DeserializeOwned{ + let ron_string = std::fs::read_to_string(path)?; + let actions = ron::from_str(&ron_string).expect("Failed to get actions from ron string"); + self.actions = actions; + //may need to clear self here but i dont really know what that does + Ok(()) + } + pub fn new_from_path(path : &str) -> std::io::Result> where T : DeserializeOwned{ + let ron_string = std::fs::read_to_string(path)?; + let actions = ron::from_str(&ron_string).expect("Failed to get actions from ron string"); + Ok(InputMap{ + actions, + ..Default::default() + }) + } +} \ No newline at end of file From 7284661fcf9c6098c0b083cefd28690ae1b8acea Mon Sep 17 00:00:00 2001 From: PhaestusFox Date: Wed, 2 Jun 2021 15:28:09 +0800 Subject: [PATCH 2/2] made review changes * added dependencies versions to .toml * made ron a dev-dependencie * changed keybinds -> keybindings *made example setup.system() one system with compile time branch * made serialize module purely for serialize feature tests * added test.keymap to .gitignore * made InputMap test one generic test that then tests String and a custom enum ? still unclear if I would need to clear the InputMap after changing the action to pickup any potential new actives/ remove actives that are no longer so *added methods to get/set/"add a group of" actions to InputMap this should allow for user impl for serialisation/de-serialisation and impl them in the example --- .gitignore | 3 +- Cargo.toml | 7 +- examples/hello_world_serde.rs | 66 ++++++++------ src/lib.rs | 39 +++++++- src/serialize.rs | 164 +++++++++++++++------------------- 5 files changed, 152 insertions(+), 127 deletions(-) diff --git a/.gitignore b/.gitignore index be8064a..2c9022d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target *.dll -Cargo.lock \ No newline at end of file +Cargo.lock +test.keymap \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index f06682e..865ae2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,12 @@ edition = "2018" [dependencies] bevy = { version = "0.5", default-features = false } -ron = { version = "*", optional = true } -serde = { version = "*", optional = true } +serde = { version = "1", optional = true } [dev-dependencies] bevy = { version = "0.5", default-features = false, features = ["bevy_winit", "x11"] } +ron = "0.6" [features] -serialize = ["ron", "bevy/serialize", "serde"] \ No newline at end of file +default = ["serialize"] +serialize = ["bevy/serialize", "serde"] \ No newline at end of file diff --git a/examples/hello_world_serde.rs b/examples/hello_world_serde.rs index 2316f98..91457c0 100644 --- a/examples/hello_world_serde.rs +++ b/examples/hello_world_serde.rs @@ -1,5 +1,6 @@ use bevy::prelude::*; use bevy_input_actionmap::*; + fn main() { App::build() .add_plugins(DefaultPlugins) @@ -17,39 +18,30 @@ enum Action { AwesomeSuperSelect, } -#[cfg(feature = "serialize")] -fn setup(mut input: ResMut>) { - if let Err(_) = input.load_from_path("keybinds.config") { +fn setup(mut input: ResMut>){ + #[cfg(feature = "serialize")] + if let Err(_) = load_from_path(&mut input, "keybindings.config") { { println!("no keybind config found creating default setup"); //just to show the path it took - input - .bind(Action::Select, KeyCode::Return) - .bind(Action::Select, GamepadButtonType::South) - .bind(Action::SuperSelect, vec![KeyCode::LAlt, KeyCode::Return]) - .bind(Action::SuperSelect, vec![KeyCode::RAlt, KeyCode::Return]) - // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. - .bind( - Action::AwesomeSuperSelect, - vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], - ); - input.save_to_path("keybinds.config").unwrap()} + create_default_keybindings(&mut input); + save_to_path(&input,"keybindings.config").unwrap()} } else {//if it loaded custom keybinds dont add new ones - println!("keybinds loaded from local file") //just to show the path it took + println!("keybindings loaded from local file") //just to show the path it took } - + #[cfg(not(feature = "serialize"))] + create_default_keybindings(&mut input); } -#[cfg(not(feature = "serialize"))] -fn setup(mut input: ResMut>){ - println!("serialize feature is off so this is the same as hello_world_enum; Why just Why"); + +fn create_default_keybindings(input : &mut ResMut>){ //this is so if you want to change default keybindings you dont need to do more then once input - .bind(Action::Select, KeyCode::Return) - .bind(Action::Select, GamepadButtonType::South) - .bind(Action::SuperSelect, vec![KeyCode::LAlt, KeyCode::Return]) - .bind(Action::SuperSelect, vec![KeyCode::RAlt, KeyCode::Return]) - // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. - .bind( - Action::AwesomeSuperSelect, - vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], - ); + .bind(Action::Select, KeyCode::Return) + .bind(Action::Select, GamepadButtonType::South) + .bind(Action::SuperSelect, vec![KeyCode::LAlt, KeyCode::Return]) + .bind(Action::SuperSelect, vec![KeyCode::RAlt, KeyCode::Return]) + // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. + .bind( + Action::AwesomeSuperSelect, + vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], + ); } fn run_commands(input: Res>) { @@ -63,3 +55,21 @@ fn run_commands(input: Res>) { println!("Awesome super selected"); } } + +fn save_to_path(input : &InputMap, path : &str)-> std::io::Result<()>{ + let mut data = Vec::new(); + for (action, bindings) in input.get_actions(){ + data.push((action, &bindings.bindings)); + } + let contents = ron::ser::to_string_pretty(&data, ron::ser::PrettyConfig::default()).expect("There was an error making the string"); + std::fs::write(path, contents)?; + Ok(()) +} + +fn load_from_path(input : &mut InputMap, path : &str) -> std::io::Result<()>{ + let ron_string = std::fs::read_to_string(path)?; + let actions = ron::from_str(&ron_string).expect("Failed to get actions from ron string"); + input.set_actions(actions); + //may need to clear self here but i dont really know what that does + Ok(()) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b5eca1b..ff369d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,9 @@ use bevy::{ prelude::*, }; +#[cfg(test)] #[cfg(feature = "serialize")] -pub mod serialize; +mod serialize; #[cfg(feature = "serialize")] pub use serde::{Serialize, Deserialize}; @@ -139,7 +140,8 @@ impl Binding { #[derive(Clone, Debug, Default, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Action { - bindings: Vec, + //add a copy of T here + pub bindings: Vec, //made pub so i can access it in example will need methods to add and remove or at the very least get &bindings if you want to serialize to be implemented by the user } impl Action { @@ -205,7 +207,8 @@ impl Action { } pub struct InputMap { - actions: HashMap, + actions: HashMap,//change this to being a new struct Bindings that does the same thing but frees up the name for Action to be use in none uniqe collections + //could also just leave the a redundent copy of T in actions pressed_buttons: HashMap, gamepad_axis: HashMap, raw_active: Vec<(T, Binding, f32)>, @@ -488,6 +491,36 @@ where input_map.wants_clear = false; } + //could we change Actions to contain a copy of T so we pass in a Vec + pub fn add_actions(&mut self, actions : Vec<(T, Vec)>){ + for (action, bindings) in actions { + self.add_action(action.clone()); + for binding in bindings{ + self.bind(action.clone(), binding); + } + } + } + + pub fn set_actions(&mut self, actions : Vec<(T, Vec)>){ + self.actions.clear(); + self.add_actions(actions) + } + + pub fn get_actions(&self) -> &HashMap{ + &self.actions + } + + pub fn get_mut_actions(&mut self) -> &mut HashMap{ + &mut self.actions + } + + pub fn get_action>(&self, key : K) -> Option<&Action>{ + self.actions.get(&key.into()) + } + + pub fn get_mut_action>(&mut self, key : K) -> Option<&mut Action>{ + self.actions.get_mut(&key.into()) + } } pub struct ActionPlugin<'a, T>(std::marker::PhantomData<&'a T>); diff --git a/src/serialize.rs b/src/serialize.rs index 7953dbc..726a9ea 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,103 +1,83 @@ -use super::*; +const ACTION_SELECT: &str = "SELECT"; +const ACTION_SUPER_SELECT: &str = "SUPER_SELECT"; +const ACTION_AWESOME_SUPER_SELECT: &str = "AWESOME_SUPER_SELECT"; +const TEST_PATH : &str = "test.keymap"; +use std::hash::Hash; +use std::fmt::Debug; +use bevy::prelude::*; use serde::{Serialize, de::DeserializeOwned}; +use crate::*; -#[cfg(test)] -mod test{ - const ACTION_SELECT: &str = "SELECT"; - const ACTION_SUPER_SELECT: &str = "SUPER_SELECT"; - const ACTION_AWESOME_SUPER_SELECT: &str = "AWESOME_SUPER_SELECT"; - const TESTPATH : &str = "test.keymap"; - use bevy::prelude::*; - use super::*; - #[derive(Hash, PartialEq, Eq, Clone, Serialize, serde::Deserialize, Debug)] - enum TestAction { - Select, - SuperSelect, - AwesomeSuperSelect, - } - fn test_binding(to_test : T) where T : Into{ - let binding : Binding = to_test.into(); - let serialized = ron::to_string(&binding).expect("Failed to serialize binding"); - let deserialized : Binding = ron::from_str(&serialized).expect("Failed to deserialize binding"); - assert_eq!(binding, deserialized); - } - - - fn test_action(action : Action){ - let serialized = ron::to_string(&action).expect("Failed to serialize action"); - let deserialized : Action = ron::from_str(&serialized).expect("Failed to deserialize action"); - assert_eq!(action, deserialized) - } - #[test] - fn test_inputmap_string(){ - let mut map = super::InputMap::::default(); - map.bind(ACTION_SELECT, KeyCode::Return) - .bind(ACTION_SELECT, GamepadButtonType::South) - .bind(ACTION_SUPER_SELECT, vec![KeyCode::LAlt, KeyCode::Return]) - .bind(ACTION_SUPER_SELECT, vec![KeyCode::RAlt, KeyCode::Return]) - // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. - .bind( - ACTION_AWESOME_SUPER_SELECT, - vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], - ); - map.save_to_path(TESTPATH).expect("Failed to save string InputMap"); - let new_map = InputMap::::new_from_path(TESTPATH).expect("Failed to load string InputMap"); - assert_eq!(map.actions, new_map.actions); - map.load_from_path(TESTPATH).expect("Failed to Load from path"); - assert_eq!(map.actions, new_map.actions); - } +#[derive(Hash, PartialEq, Eq, Clone, Serialize, serde::Deserialize, Debug)] +enum TestAction { + Select, + SuperSelect, + AwesomeSuperSelect, +} +fn test_binding(to_test : T) where T : Into{ + let binding : Binding = to_test.into(); + let serialized = ron::to_string(&binding).expect("Failed to serialize binding"); + let deserialized : Binding = ron::from_str(&serialized).expect("Failed to deserialize binding"); + assert_eq!(binding, deserialized); +} - fn test_inputmap_enum(){ - let mut map = super::InputMap::::default(); - map.bind(TestAction::Select, KeyCode::Return) - .bind(TestAction::Select, GamepadButtonType::South) - .bind(TestAction::SuperSelect, vec![KeyCode::LAlt, KeyCode::Return]) - .bind(TestAction::SuperSelect, vec![KeyCode::RAlt, KeyCode::Return]) - // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. - .bind( - TestAction::AwesomeSuperSelect, - vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], - ); - map.save_to_path(TESTPATH).expect("Failed to save enum InputMap"); - let new_map = InputMap::::new_from_path(TESTPATH).expect("Failed to load enum InputMap"); - assert_eq!(map.actions, new_map.actions); - map.load_from_path(TESTPATH).expect("Failed to Load from path"); - assert_eq!(map.actions, new_map.actions); - } - - #[test] - fn it_works(){ - test_binding(KeyCode::Space); - test_binding(vec![KeyCode::Space, KeyCode::LControl]); - test_binding(GamepadButtonType::North); - test_binding(vec![GamepadButtonType::North, GamepadButtonType::LeftThumb]); - test_action(Action{ - bindings : vec![KeyCode::Space.into(), KeyCode::J.into()] - }); - test_inputmap_string(); - test_inputmap_enum(); - std::fs::remove_file(TESTPATH).expect("Failed to remove testsave file"); - } + +fn test_action(action : Action){ + let serialized = ron::to_string(&action).expect("Failed to serialize action"); + let deserialized : Action = ron::from_str(&serialized).expect("Failed to deserialize action"); + assert_eq!(action, deserialized) } -impl InputMap { - pub fn save_to_path(&self, path : &str) -> std::io::Result<()> where T : Serialize{ - let contents = ron::ser::to_string_pretty(&self.actions, ron::ser::PrettyConfig::default()).expect("There was an error making the string"); - std::fs::write(path, contents) +fn test_inputmap(action_0 : T, action_1 : T, action_2 : T) +where T : Debug + PartialEq + Eq + Hash + Clone + Send + Sync + Serialize + DeserializeOwned{ + let mut map = InputMap::::default(); + map.bind(action_0.clone(), KeyCode::Return) + .bind(action_0, GamepadButtonType::South) + .bind(action_1.clone(), vec![KeyCode::LAlt, KeyCode::Return]) + .bind(action_1, vec![KeyCode::RAlt, KeyCode::Return]) + // This should bind left/right control and left/right alt, but the combos would get ridiculous so hopefully this is sufficient. + .bind( + action_2, + vec![KeyCode::LAlt, KeyCode::LControl, KeyCode::Return], + ); + save_to_path(&mut map, TEST_PATH).expect("Failed to save InputMap"); + let mut new_map = InputMap::::default(); + load_from_path(&mut new_map, TEST_PATH).expect("Failed to load InputMap"); + assert_eq!(map.actions, new_map.actions); + load_from_path(&mut map, TEST_PATH).expect("Failed to Load from path"); + assert_eq!(map.actions, new_map.actions); +} + +#[test] +fn it_works(){ + test_binding(KeyCode::Space); + test_binding(vec![KeyCode::Space, KeyCode::LControl]); + test_binding(GamepadButtonType::North); + test_binding(vec![GamepadButtonType::North, GamepadButtonType::LeftThumb]); + test_action(Action{ + bindings : vec![KeyCode::Space.into(), KeyCode::J.into()] + }); + test_inputmap::(ACTION_SELECT.to_string(), ACTION_SUPER_SELECT.to_string(), ACTION_AWESOME_SUPER_SELECT.to_string()); + test_inputmap::(TestAction::Select, TestAction::SuperSelect, TestAction::AwesomeSuperSelect); + std::fs::remove_file(TEST_PATH).expect("Failed to remove testsave file"); +} + +fn save_to_path(input : &InputMap, path : &str)-> std::io::Result<()> +where T : Debug + PartialEq + Eq + Hash + Clone + Send + Sync + Serialize{ + let mut data = Vec::new(); + for (action, bindings) in input.get_actions(){ + data.push((action, &bindings.bindings)); } - pub fn load_from_path(&mut self, path : &str) -> std::io::Result<()> where T : DeserializeOwned{ + let contents = ron::ser::to_string_pretty(&data, ron::ser::PrettyConfig::default()).expect("There was an error making the string"); + std::fs::write(path, contents)?; + Ok(()) +} + +fn load_from_path(input : &mut InputMap, path : &str) -> std::io::Result<()> +where T : Debug + PartialEq + Eq + Hash + Clone + Send + Sync + DeserializeOwned{ let ron_string = std::fs::read_to_string(path)?; let actions = ron::from_str(&ron_string).expect("Failed to get actions from ron string"); - self.actions = actions; + input.set_actions(actions); //may need to clear self here but i dont really know what that does Ok(()) - } - pub fn new_from_path(path : &str) -> std::io::Result> where T : DeserializeOwned{ - let ron_string = std::fs::read_to_string(path)?; - let actions = ron::from_str(&ron_string).expect("Failed to get actions from ron string"); - Ok(InputMap{ - actions, - ..Default::default() - }) - } } \ No newline at end of file