diff --git a/file_linked/Cargo.toml b/file_linked/Cargo.toml index abf3367..3a4e5b6 100644 --- a/file_linked/Cargo.toml +++ b/file_linked/Cargo.toml @@ -19,4 +19,5 @@ serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" anyhow = "1.0" bincode = "1.3.3" -log = "0.4.14" \ No newline at end of file +log = "0.4.14" +serde_json = "1.0.114" diff --git a/file_linked/src/constants/data_format.rs b/file_linked/src/constants/data_format.rs new file mode 100644 index 0000000..a50f00e --- /dev/null +++ b/file_linked/src/constants/data_format.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub enum DataFormat { + Bincode, + Json +} \ No newline at end of file diff --git a/file_linked/src/constants/mod.rs b/file_linked/src/constants/mod.rs new file mode 100644 index 0000000..e01d262 --- /dev/null +++ b/file_linked/src/constants/mod.rs @@ -0,0 +1 @@ +pub mod data_format; \ No newline at end of file diff --git a/file_linked/src/lib.rs b/file_linked/src/lib.rs index 5b73c1d..de7e075 100644 --- a/file_linked/src/lib.rs +++ b/file_linked/src/lib.rs @@ -1,8 +1,10 @@ //! A wrapper around an object that ties it to a physical file pub mod error; +pub mod constants; use anyhow::{anyhow, Context}; +use constants::data_format::DataFormat; use error::Error; use log::info; use serde::{de::DeserializeOwned, Serialize}; @@ -14,6 +16,8 @@ use std::{ thread::JoinHandle, }; + + /// A wrapper around an object `T` that ties the object to a physical file #[derive(Debug)] pub struct FileLinked @@ -24,6 +28,7 @@ where path: PathBuf, temp_file_path: PathBuf, file_thread: Option>, + data_format: DataFormat, } impl Drop for FileLinked @@ -48,6 +53,7 @@ where /// # Examples /// ``` /// # use file_linked::*; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -67,7 +73,7 @@ where /// c: 3.0 /// }; /// - /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -88,6 +94,7 @@ where /// # Examples /// ``` /// # use file_linked::*; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -107,7 +114,7 @@ where /// c: 3.0 /// }; /// - /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -119,7 +126,7 @@ where /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` - pub fn new(val: T, path: &Path) -> Result, Error> { + pub fn new(val: T, path: &Path, data_format: DataFormat) -> Result, Error> { let mut temp_file_path = path.to_path_buf(); temp_file_path.set_file_name(format!( ".temp{}", @@ -134,6 +141,7 @@ where path: path.to_path_buf(), temp_file_path, file_thread: None, + data_format }; result.write_data()?; @@ -143,8 +151,12 @@ where fn write_data(&mut self) -> Result<(), Error> { let thread_path = self.path.clone(); let thread_temp_path = self.temp_file_path.clone(); - let thread_val = bincode::serialize(&self.val) - .with_context(|| "Unable to serialize object into bincode".to_string())?; + let thread_val = match self.data_format { + DataFormat::Bincode => bincode::serialize(&self.val) + .with_context(|| "Unable to serialize object into bincode".to_string())?, + DataFormat::Json => serde_json::to_vec(&self.val) + .with_context(|| "Unable to serialize object into JSON".to_string())?, + }; if let Some(file_thread) = self.file_thread.take() { file_thread @@ -190,6 +202,7 @@ where /// ``` /// # use file_linked::*; /// # use file_linked::error::Error; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -209,7 +222,7 @@ where /// c: 0.0 /// }; /// - /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -239,6 +252,7 @@ where /// ``` /// # use file_linked::*; /// # use file_linked::error::Error; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -258,7 +272,7 @@ where /// c: 0.0 /// }; /// - /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -295,6 +309,7 @@ where /// ``` /// # use file_linked::*; /// # use file_linked::error::Error; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -327,7 +342,7 @@ where /// /// bincode::serialize_into(file, &test).expect("Unable to serialize object"); /// - /// let mut linked_test = FileLinked::::from_file(&path) + /// let mut linked_test = FileLinked::::from_file(&path, DataFormat::Bincode) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, test.a); @@ -341,7 +356,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn from_file(path: &Path) -> Result, Error> { + pub fn from_file(path: &Path, data_format: DataFormat) -> Result, Error> { let mut temp_file_path = path.to_path_buf(); temp_file_path.set_file_name(format!( ".temp{}", @@ -352,15 +367,21 @@ where )); match File::open(path).map_err(Error::from).and_then(|file| { - bincode::deserialize_from::(file) - .with_context(|| format!("Unable to deserialize file {}", path.display())) - .map_err(Error::from) + match data_format { + DataFormat::Bincode => bincode::deserialize_from::(file) + .with_context(|| format!("Unable to deserialize file {}", path.display())) + .map_err(Error::from), + DataFormat::Json => serde_json::from_reader(file) + .with_context(|| format!("Unable to deserialize file {}", path.display())) + .map_err(Error::from), + } }) { Ok(val) => Ok(FileLinked { val, path: path.to_path_buf(), temp_file_path, file_thread: None, + data_format, }), Err(err) => { info!( @@ -370,7 +391,7 @@ where ); // Try to use temp file instead and see if that file exists and is serializable - let val = FileLinked::from_temp_file(&temp_file_path, path) + let val = FileLinked::from_temp_file(&temp_file_path, path, &data_format) .map_err(|_| err) .with_context(|| format!("Failed to read/deserialize the object from the file {} and temp file {}", path.display(), temp_file_path.display()))?; @@ -379,21 +400,30 @@ where path: path.to_path_buf(), temp_file_path, file_thread: None, + data_format, }) } } } - fn from_temp_file(temp_file_path: &Path, path: &Path) -> Result { + fn from_temp_file(temp_file_path: &Path, path: &Path, data_format: &DataFormat) -> Result { let file = File::open(temp_file_path) .with_context(|| format!("Unable to open file {}", temp_file_path.display()))?; - let val = bincode::deserialize_from(file).with_context(|| { - format!( - "Could not deserialize from temp file {}", - temp_file_path.display() - ) - })?; + let val = match data_format { + DataFormat::Bincode => bincode::deserialize_from(file).with_context(|| { + format!( + "Could not deserialize from temp file {}", + temp_file_path.display() + ) + })?, + DataFormat::Json => serde_json::from_reader(file).with_context(|| { + format!( + "Could not deserialize from temp file {}", + temp_file_path.display() + ) + })?, + }; info!("Successfully deserialized value from temp file"); @@ -441,7 +471,7 @@ mod tests { cleanup.run(|p| { let val = vec!["one", "two", ""]; - let linked_object = FileLinked::new(val.clone(), &p)?; + let linked_object = FileLinked::new(val.clone(), &p, DataFormat::Json)?; assert_eq!(*linked_object.readonly(), val); Ok(()) @@ -455,7 +485,7 @@ mod tests { cleanup.run(|p| { let val = "test"; - FileLinked::new(val, &p)?; + FileLinked::new(val, &p, DataFormat::Bincode)?; let file = File::open(&p)?; let result: String = @@ -472,7 +502,7 @@ mod tests { let cleanup = CleanUp::new(&path); cleanup.run(|p| { let list = vec![1, 2, 3, 4]; - let mut file_linked_list = FileLinked::new(list, &p)?; + let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json)?; assert_eq!(*file_linked_list.readonly(), vec![1, 2, 3, 4]); file_linked_list.mutate(|v1| v1.push(5))?; @@ -493,7 +523,7 @@ mod tests { cleanup.run(|p| { let val1 = String::from("val1"); let val2 = String::from("val2"); - let mut file_linked_list = FileLinked::new(val1.clone(), &p)?; + let mut file_linked_list = FileLinked::new(val1.clone(), &p, DataFormat::Bincode)?; assert_eq!(*file_linked_list.readonly(), val1); file_linked_list.replace(val2.clone())?; @@ -515,7 +545,7 @@ mod tests { bincode::serialize_into(&file, &value).expect("Unable to serialize into file"); drop(file); - let linked_object: FileLinked> = FileLinked::from_file(&p)?; + let linked_object: FileLinked> = FileLinked::from_file(&p, DataFormat::Bincode)?; assert_eq!(*linked_object.readonly(), value); drop(linked_object); diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index c12cd33..2618232 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -6,6 +6,7 @@ extern crate log; mod test_state; use easy_parallel::Parallel; +use file_linked::constants::data_format::DataFormat; use gemla::{ core::{Gemla, GemlaConfig}, error::{log_error, Error}, @@ -57,6 +58,7 @@ fn main() -> anyhow::Result<()> { generations_per_node: 3, overwrite: true, }, + DataFormat::Json, ))?; log_error(gemla.simulate(3).await)?; diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 4a75b6c..64f006f 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -4,7 +4,7 @@ pub mod genetic_node; use crate::{error::Error, tree::Tree}; -use file_linked::FileLinked; +use file_linked::{constants::data_format::DataFormat, FileLinked}; use futures::{future, future::BoxFuture}; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; @@ -77,21 +77,21 @@ impl<'a, T: 'a> Gemla<'a, T> where T: GeneticNode + Serialize + DeserializeOwned + Debug + Clone + Send, { - pub fn new(path: &Path, config: GemlaConfig) -> Result { + pub fn new(path: &Path, config: GemlaConfig, data_format: DataFormat) -> Result { match File::open(path) { // If the file exists we either want to overwrite the file or read from the file // based on the configuration provided Ok(_) => Ok(Gemla { data: if config.overwrite { - FileLinked::new((None, config), path)? + FileLinked::new((None, config), path, data_format)? } else { - FileLinked::from_file(path)? + FileLinked::from_file(path, data_format)? }, threads: HashMap::new(), }), // If the file doesn't exist we must create it Err(error) if error.kind() == ErrorKind::NotFound => Ok(Gemla { - data: FileLinked::new((None, config), path)?, + data: FileLinked::new((None, config), path, data_format)?, threads: HashMap::new(), }), Err(error) => Err(Error::IO(error)), @@ -400,7 +400,7 @@ mod tests { generations_per_node: 1, overwrite: true }; - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(2))?; assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); @@ -409,7 +409,7 @@ mod tests { assert!(path.exists()); // Testing overwriting data - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(2))?; assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); @@ -419,7 +419,7 @@ mod tests { // Testing not-overwriting data config.overwrite = false; - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(2))?; assert_eq!(gemla.tree_ref().unwrap().height(), 4); @@ -440,7 +440,7 @@ mod tests { generations_per_node: 10, overwrite: true }; - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(5))?; let tree = gemla.tree_ref().unwrap();