From edc2f678976c2917360d171ad7339043d9be6440 Mon Sep 17 00:00:00 2001 From: vandomej Date: Sat, 2 Oct 2021 17:13:01 -0700 Subject: [PATCH] Revising error types --- file_linked/Cargo.toml | 4 +- file_linked/src/lib.rs | 58 +++++++++++------ gemla/Cargo.toml | 4 +- gemla/src/bracket/genetic_node.rs | 103 ++++++++++++++++-------------- gemla/src/bracket/mod.rs | 84 ++++++++++-------------- gemla/src/error.rs | 18 ++++++ gemla/src/lib.rs | 1 + gemla/src/tree/mod.rs | 13 ---- 8 files changed, 153 insertions(+), 132 deletions(-) create mode 100644 gemla/src/error.rs diff --git a/file_linked/Cargo.toml b/file_linked/Cargo.toml index 2ff07ab..5947284 100644 --- a/file_linked/Cargo.toml +++ b/file_linked/Cargo.toml @@ -15,4 +15,6 @@ categories = ["filesystem", "data-structures"] [dependencies] serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" \ No newline at end of file +serde_json = "1.0" +thiserror = "1.0" +anyhow = "1.0" \ No newline at end of file diff --git a/file_linked/src/lib.rs b/file_linked/src/lib.rs index 629d6bd..7ccd138 100644 --- a/file_linked/src/lib.rs +++ b/file_linked/src/lib.rs @@ -4,11 +4,24 @@ extern crate serde; use std::fmt; use std::fs; +use std::io; use std::io::prelude::*; use std::path; +use anyhow::{anyhow, Context}; use serde::de::DeserializeOwned; use serde::Serialize; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error(transparent)] + Serialization(serde_json::Error), + #[error(transparent)] + IO(std::io::Error), + #[error(transparent)] + Other(#[from] anyhow::Error), +} /// A wrapper around an object `T` that ties the object to a physical file pub struct FileLinked @@ -95,9 +108,12 @@ where /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` - pub fn new(val: T, path: path::PathBuf) -> Result, String> { + pub fn new(val: T, path: path::PathBuf) -> Result, Error> { if path.is_file() { - return Err(format!("{} is not a valid file path", path.display())); + return Err(Error::IO(io::Error::new( + io::ErrorKind::Other, + anyhow!("{} is not a valid file path", path.display()), + ))); } let result = FileLinked { val, path }; @@ -107,20 +123,20 @@ where Ok(result) } - fn write_data(&self) -> Result<(), String> { + fn write_data(&self) -> Result<(), Error> { let mut file = fs::OpenOptions::new() .write(true) .create(true) .truncate(true) .open(&self.path) - .map_err(|_| format!("Unable to open path {}", self.path.display()))?; + .with_context(|| format!("Unable to open path {}", self.path.display()))?; write!( file, "{}", - serde_json::to_string(&self.val).map_err(|e| e.to_string())? + serde_json::to_string(&self.val).map_err(Error::Serialization)? ) - .or_else(|_| Err(String::from("Unable to write to file.")))?; + .with_context(|| format!("Unable to write to file {}", self.path.display()))?; Ok(()) } @@ -143,7 +159,7 @@ where /// # pub c: f64 /// # } /// # - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let test = Test { /// a: 1, /// b: String::from(""), @@ -164,7 +180,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn mutate U>(&mut self, op: F) -> Result { + pub fn mutate U>(&mut self, op: F) -> Result { let result = op(&mut self.val); self.write_data()?; @@ -189,7 +205,7 @@ where /// # pub c: f64 /// # } /// # - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let test = Test { /// a: 1, /// b: String::from(""), @@ -214,7 +230,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn replace(&mut self, val: T) -> Result<(), String> { + pub fn replace(&mut self, val: T) -> Result<(), Error> { self.val = val; self.write_data() @@ -245,7 +261,7 @@ where /// # pub c: f64 /// # } /// # - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let test = Test { /// a: 1, /// b: String::from("2"), @@ -278,27 +294,33 @@ where /// # Ok(()) /// # } /// ``` - pub fn from_file(path: path::PathBuf) -> Result, String> { + pub fn from_file(path: path::PathBuf) -> Result, Error> { if !path.is_file() { - return Err(format!("{} is not a valid file path", path.display())); + return Err(Error::IO(io::Error::new( + io::ErrorKind::Other, + anyhow!("{} is not a valid file path", path.display()), + ))); } let metadata = path .metadata() - .map_err(|_| format!("Error obtaining metadata for {}", path.display()))?; + .with_context(|| format!("Error obtaining metadata for {}", path.display()))?; if metadata.is_file() { let file = fs::OpenOptions::new() .read(true) .open(&path) - .map_err(|_| format!("Unable to open file {}", path.display()))?; + .with_context(|| format!("Unable to open file {}", path.display()))?; let val = serde_json::from_reader(file) - .map_err(|_| String::from("Unable to parse value from file."))?; + .with_context(|| String::from("Unable to parse value from file."))?; Ok(FileLinked { val, path }) } else { - Err(format!("{} is not a file.", path.display())) + return Err(Error::IO(io::Error::new( + io::ErrorKind::Other, + anyhow!("{} is not a file.", path.display()), + ))); } } } @@ -318,7 +340,7 @@ mod tests { use std::fs; #[test] - fn test_mutate() -> Result<(), String> { + fn test_mutate() -> Result<(), Error> { let list = vec![1, 2, 3, 4]; let mut file_linked_list = FileLinked::new(list, path::PathBuf::from("test.txt"))?; diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index 72a5454..93a0e72 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -18,4 +18,6 @@ uuid = { version = "0.7", features = ["serde", "v4"] } clap = { version = "~2.27.0", features = ["yaml"] } toml = "0.5.8" regex = "1" -file_linked = { version = "0.1.0", path = "../file_linked" } \ No newline at end of file +file_linked = { version = "0.1.0", path = "../file_linked" } +thiserror = "1.0" +anyhow = "1.0" \ No newline at end of file diff --git a/gemla/src/bracket/genetic_node.rs b/gemla/src/bracket/genetic_node.rs index 9caa3fe..dcc10d9 100644 --- a/gemla/src/bracket/genetic_node.rs +++ b/gemla/src/bracket/genetic_node.rs @@ -2,6 +2,9 @@ //! //! [`Bracket`]: crate::bracket::Bracket +use crate::error::Error; + +use anyhow::Context; use serde::{Deserialize, Serialize}; use std::fmt; @@ -33,19 +36,20 @@ pub trait GeneticNode { /// /// ``` /// # use gemla::bracket::genetic_node::GeneticNode; + /// # use gemla::error::Error; /// # /// struct Node { /// pub fit_score: f64, /// } /// /// impl GeneticNode for Node { - /// fn initialize() -> Result, String> { + /// fn initialize() -> Result, Error> { /// Ok(Box::new(Node {fit_score: 0.0})) /// } /// /// //... /// # - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # Ok(()) /// # } /// # @@ -53,22 +57,22 @@ pub trait GeneticNode { /// # self.fit_score /// # } /// # - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// } /// - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let node = Node::initialize()?; /// assert_eq!(node.get_fit_score(), 0.0); /// # Ok(()) /// # } /// ``` - fn initialize() -> Result, String>; + fn initialize() -> Result, Error>; /// Runs a simulation on the state object for the given number of `iterations` in order to guage it's fitness. /// This will be called for every node in a bracket before evaluating it's fitness against other nodes. @@ -77,6 +81,7 @@ pub trait GeneticNode { /// /// ``` /// # use gemla::bracket::genetic_node::GeneticNode; + /// # use gemla::error::Error; /// # /// struct Model { /// pub fit_score: f64, @@ -89,7 +94,7 @@ pub trait GeneticNode { /// } /// /// impl Model { - /// fn fit(&mut self, epochs: u64) -> Result<(), String> { + /// fn fit(&mut self, epochs: u64) -> Result<(), Error> { /// //... /// # self.fit_score += epochs as f64; /// # Ok(()) @@ -97,13 +102,13 @@ pub trait GeneticNode { /// } /// /// impl GeneticNode for Node { - /// # fn initialize() -> Result, String> { + /// # fn initialize() -> Result, Error> { /// # Ok(Box::new(Node {models: vec![Model {fit_score: 0.0}]})) /// # } /// # /// //... /// - /// fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// for m in self.models.iter_mut() /// { /// m.fit(iterations)?; @@ -117,23 +122,23 @@ pub trait GeneticNode { /// # self.models.iter().max_by(|m1, m2| m1.fit_score.partial_cmp(&m2.fit_score).unwrap()).unwrap().fit_score /// # } /// # - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// } /// - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let mut node = Node::initialize()?; /// node.simulate(5)?; /// assert_eq!(node.get_fit_score(), 5.0); /// # Ok(()) /// # } /// ``` - fn simulate(&mut self, iterations: u64) -> Result<(), String>; + fn simulate(&mut self, iterations: u64) -> Result<(), Error>; /// Returns a fit score associated with the nodes performance. /// This will be used by a bracket in order to determine the most successful child. @@ -141,6 +146,7 @@ pub trait GeneticNode { /// # Examples /// ``` /// # use gemla::bracket::genetic_node::GeneticNode; + /// # use gemla::error::Error; /// # /// struct Model { /// pub fit_score: f64, @@ -153,7 +159,7 @@ pub trait GeneticNode { /// } /// /// # impl Model { - /// # fn fit(&mut self, epochs: u64) -> Result<(), String> { + /// # fn fit(&mut self, epochs: u64) -> Result<(), Error> { /// # //... /// # self.fit_score += epochs as f64; /// # Ok(()) @@ -161,13 +167,13 @@ pub trait GeneticNode { /// # } /// /// impl GeneticNode for Node { - /// # fn initialize() -> Result, String> { + /// # fn initialize() -> Result, Error> { /// # Ok(Box::new(Node {models: vec![Model {fit_score: 0.0}]})) /// # } /// # /// # //... /// # - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # for m in self.models.iter_mut() /// # { /// # m.fit(iterations)?; @@ -182,16 +188,16 @@ pub trait GeneticNode { /// } /// /// //... - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// } /// - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let mut node = Node::initialize()?; /// node.simulate(5)?; /// assert_eq!(node.get_fit_score(), 5.0); @@ -205,6 +211,7 @@ pub trait GeneticNode { /// # Examples /// ``` /// # use gemla::bracket::genetic_node::GeneticNode; + /// # use gemla::error::Error; /// # /// struct Model { /// pub fit_score: f64, @@ -218,7 +225,7 @@ pub trait GeneticNode { /// } /// /// # impl Model { - /// # fn fit(&mut self, epochs: u64) -> Result<(), String> { + /// # fn fit(&mut self, epochs: u64) -> Result<(), Error> { /// # //... /// # self.fit_score += epochs as f64; /// # Ok(()) @@ -226,7 +233,7 @@ pub trait GeneticNode { /// # } /// /// impl GeneticNode for Node { - /// # fn initialize() -> Result, String> { + /// # fn initialize() -> Result, Error> { /// # Ok(Box::new(Node { /// # models: vec![ /// # Model { fit_score: 0.0 }, @@ -241,7 +248,7 @@ pub trait GeneticNode { /// # /// # //... /// # - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # for m in self.models.iter_mut() { /// # m.fit(iterations)?; /// # } @@ -258,7 +265,7 @@ pub trait GeneticNode { /// # .fit_score /// # } /// # - /// fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// self.models.sort_by(|a, b| a.fit_score.partial_cmp(&b.fit_score).unwrap().reverse()); /// self.models.truncate(3); /// Ok(()) @@ -266,12 +273,12 @@ pub trait GeneticNode { /// /// //... /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// } /// - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let mut node = Node::initialize()?; /// assert_eq!(node.models.len(), 5); /// @@ -283,13 +290,14 @@ pub trait GeneticNode { /// # Ok(()) /// # } /// ``` - fn calculate_scores_and_trim(&mut self) -> Result<(), String>; + fn calculate_scores_and_trim(&mut self) -> Result<(), Error>; /// Mutates members in a population and/or crossbreeds them to produce new offspring. /// /// # Examples /// ``` /// # use gemla::bracket::genetic_node::GeneticNode; + /// # use gemla::error::Error; /// # use std::convert::TryInto; /// # /// struct Model { @@ -304,7 +312,7 @@ pub trait GeneticNode { /// } /// /// # impl Model { - /// # fn fit(&mut self, epochs: u64) -> Result<(), String> { + /// # fn fit(&mut self, epochs: u64) -> Result<(), Error> { /// # //... /// # self.fit_score += epochs as f64; /// # Ok(()) @@ -320,7 +328,7 @@ pub trait GeneticNode { /// } /// /// impl GeneticNode for Node { - /// # fn initialize() -> Result, String> { + /// # fn initialize() -> Result, Error> { /// # Ok(Box::new(Node { /// # models: vec![ /// # Model { fit_score: 0.0 }, @@ -333,7 +341,7 @@ pub trait GeneticNode { /// # })) /// # } /// # - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # for m in self.models.iter_mut() { /// # m.fit(iterations)?; /// # } @@ -348,14 +356,14 @@ pub trait GeneticNode { /// # .fit_score /// # } /// # - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # self.models.sort_by(|a, b| a.fit_score.partial_cmp(&b.fit_score).unwrap().reverse()); /// # self.models.truncate(3); /// # Ok(()) /// # } /// //... /// - /// fn mutate(&mut self) -> Result<(), String> { + /// fn mutate(&mut self) -> Result<(), Error> { /// loop { /// if self.models.len() < self.population_size.try_into().unwrap() /// { @@ -368,7 +376,7 @@ pub trait GeneticNode { /// } /// } /// - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let mut node = Node::initialize()?; /// assert_eq!(node.models.len(), 5); /// @@ -383,7 +391,7 @@ pub trait GeneticNode { /// # Ok(()) /// # } /// ``` - fn mutate(&mut self) -> Result<(), String>; + fn mutate(&mut self) -> Result<(), Error>; } /// Used externally to wrap a node implementing the [`GeneticNode`] trait. Processes state transitions for the given node as @@ -410,6 +418,7 @@ where /// ``` /// # use gemla::bracket::genetic_node::GeneticNode; /// # use gemla::bracket::genetic_node::GeneticNodeWrapper; + /// # use gemla::error::Error; /// # #[derive(Debug)] /// struct Node { /// # pub fit_score: f64, @@ -418,12 +427,12 @@ where /// /// impl GeneticNode for Node { /// //... - /// # fn initialize() -> Result, String> { + /// # fn initialize() -> Result, Error> { /// # Ok(Box::new(Node {fit_score: 0.0})) /// # } /// # /// # - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # Ok(()) /// # } /// # @@ -431,22 +440,22 @@ where /// # self.fit_score /// # } /// # - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// } /// - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), Error> { /// let mut wrapped_node = GeneticNodeWrapper::::new()?; /// assert_eq!(wrapped_node.data.unwrap().get_fit_score(), 0.0); /// # Ok(()) /// # } /// ``` - pub fn new() -> Result { + pub fn new() -> Result { let mut node = GeneticNodeWrapper { data: None, state: GeneticState::Initialize, @@ -476,14 +485,14 @@ where /// [`simulate`]: crate::bracket::genetic_node::GeneticNode#tymethod.simulate /// [`calculate_scores_and_trim`]: crate::bracket::genetic_node::GeneticNode#tymethod.calculate_scores_and_trim /// [`mutate`]: crate::bracket::genetic_node::GeneticNode#tymethod.mutate - pub fn process_node(&mut self, iterations: u32) -> Result<(), String> { + pub fn process_node(&mut self, iterations: u32) -> Result<(), Error> { // Looping through each state transition until the number of iterations have been reached. loop { - match (self.state, self.data.as_ref()) { + match (self.state, &self.data) { (GeneticState::Initialize, _) => { self.iteration = 0; - let new_data = - T::initialize().map_err(|e| format!("Error initializing node: {}", e))?; + let new_data = T::initialize() + .with_context(|| format!("Error initializing node {:?}", self))?; self.data = Some(*new_data); self.state = GeneticState::Simulate; } @@ -492,7 +501,7 @@ where .as_mut() .unwrap() .simulate(5) - .map_err(|e| format!("Error simulating node: {}", e))?; + .with_context(|| format!("Error simulating node: {:?}", self))?; self.state = GeneticState::Score; } (GeneticState::Score, Some(_)) => { @@ -500,7 +509,7 @@ where .as_mut() .unwrap() .calculate_scores_and_trim() - .map_err(|e| format!("Error scoring and trimming node: {}", e))?; + .with_context(|| format!("Error scoring and trimming node: {:?}", self))?; self.state = if self.iteration == iterations { GeneticState::Finish @@ -513,13 +522,13 @@ where .as_mut() .unwrap() .mutate() - .map_err(|e| format!("Error mutating node: {}", e))?; + .with_context(|| format!("Error mutating node: {:?}", self))?; self.state = GeneticState::Simulate; } (GeneticState::Finish, Some(_)) => { break; } - _ => return Err(format!("Error processing node {:?}", self.data)), + _ => panic!("Error processing node {:?}", self.data), } } diff --git a/gemla/src/bracket/mod.rs b/gemla/src/bracket/mod.rs index e87db18..98a47e9 100644 --- a/gemla/src/bracket/mod.rs +++ b/gemla/src/bracket/mod.rs @@ -3,14 +3,14 @@ pub mod genetic_node; -use super::tree; +use crate::error::Error; +use crate::tree; use file_linked::FileLinked; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::fmt; use std::path; -use std::str::FromStr; /// As the bracket tree increases in height, `IterationScaling` can be used to configure the number of iterations that /// a node runs for. @@ -19,6 +19,7 @@ use std::str::FromStr; /// /// ``` /// # use gemla::bracket::*; +/// # use gemla::error::Error; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::str::FromStr; @@ -30,14 +31,6 @@ use std::str::FromStr; /// # pub score: f64, /// # } /// # -/// # impl FromStr for TestState { -/// # type Err = String; -/// # -/// # fn from_str(s: &str) -> Result { -/// # serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) -/// # } -/// # } -/// # /// # impl TestState { /// # fn new(score: f64) -> TestState { /// # TestState { score: score } @@ -45,7 +38,7 @@ use std::str::FromStr; /// # } /// # /// # impl genetic_node::GeneticNode for TestState { -/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { +/// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # self.score += iterations as f64; /// # Ok(()) /// # } @@ -54,15 +47,15 @@ use std::str::FromStr; /// # self.score /// # } /// # -/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { +/// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # -/// # fn mutate(&mut self) -> Result<(), String> { +/// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # -/// # fn initialize() -> Result, String> { +/// # fn initialize() -> Result, Error> { /// # Ok(Box::new(TestState { score: 0.0 })) /// # } /// # } @@ -135,7 +128,7 @@ where impl Bracket where - T: genetic_node::GeneticNode + FromStr + Default + DeserializeOwned + Serialize + Clone, + T: genetic_node::GeneticNode + Default + DeserializeOwned + Serialize + Clone, { /// Initializes a bracket of type `T` storing the contents to `file_path` /// @@ -143,6 +136,7 @@ where /// ``` /// # use gemla::bracket::*; /// # use gemla::btree; + /// # use gemla::error::Error; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::str::FromStr; @@ -175,7 +169,7 @@ where /// } /// /// impl genetic_node::GeneticNode for TestState { - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # self.score += iterations as f64; /// # Ok(()) /// # } @@ -184,15 +178,15 @@ where /// # self.score /// # } /// # - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// fn initialize() -> Result, String> { + /// fn initialize() -> Result, Error> { /// Ok(Box::new(TestState { score: 0.0 })) /// } /// @@ -212,14 +206,14 @@ where /// std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` - pub fn initialize(file_path: path::PathBuf) -> Result, String> { - FileLinked::new( + pub fn initialize(file_path: path::PathBuf) -> Result, Error> { + Ok(FileLinked::new( Bracket { tree: btree!(*T::initialize()?), iteration_scaling: IterationScaling::default(), }, file_path, - ) + )?) } /// Given a bracket object, configures it's [`IterationScaling`]. @@ -227,6 +221,7 @@ where /// # Examples /// ``` /// # use gemla::bracket::*; + /// # use gemla::error::Error; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::str::FromStr; @@ -238,14 +233,6 @@ where /// # pub score: f64, /// # } /// # - /// # impl FromStr for TestState { - /// # type Err = String; - /// # - /// # fn from_str(s: &str) -> Result { - /// # serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) - /// # } - /// # } - /// # /// # impl fmt::Display for TestState { /// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { /// # write!(f, "{}", self.score) @@ -259,7 +246,7 @@ where /// # } /// # /// # impl genetic_node::GeneticNode for TestState { - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # self.score += iterations as f64; /// # Ok(()) /// # } @@ -268,15 +255,15 @@ where /// # self.score /// # } /// # - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn initialize() -> Result, String> { + /// # fn initialize() -> Result, Error> { /// # Ok(Box::new(TestState { score: 0.0 })) /// # } /// # } @@ -300,7 +287,7 @@ where // Creates a balanced tree with the given `height` that will be used as a branch of the primary tree. // This additionally simulates and evaluates nodes in the branch as it is built. - fn create_new_branch(&self, height: u64) -> Result, String> { + fn create_new_branch(&self, height: u64) -> Result, Error> { if height == 1 { let mut base_node = btree!(*T::initialize()?); @@ -337,6 +324,7 @@ where /// # Examples /// ``` /// # use gemla::bracket::*; + /// # use gemla::error::Error; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::str::FromStr; @@ -348,14 +336,6 @@ where /// # pub score: f64, /// # } /// # - /// # impl FromStr for TestState { - /// # type Err = String; - /// # - /// # fn from_str(s: &str) -> Result { - /// # serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) - /// # } - /// # } - /// # /// # impl fmt::Display for TestState { /// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { /// # write!(f, "{}", self.score) @@ -369,7 +349,7 @@ where /// # } /// # /// # impl genetic_node::GeneticNode for TestState { - /// # fn simulate(&mut self, iterations: u64) -> Result<(), String> { + /// # fn simulate(&mut self, iterations: u64) -> Result<(), Error> { /// # self.score += iterations as f64; /// # Ok(()) /// # } @@ -378,15 +358,15 @@ where /// # self.score /// # } /// # - /// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn mutate(&mut self) -> Result<(), String> { + /// # fn mutate(&mut self) -> Result<(), Error> { /// # Ok(()) /// # } /// # - /// # fn initialize() -> Result, String> { + /// # fn initialize() -> Result, Error> { /// # Ok(Box::new(TestState { score: 0.0 })) /// # } /// # } @@ -407,7 +387,7 @@ where /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` - pub fn run_simulation_step(&mut self) -> Result<&mut Self, String> { + pub fn run_simulation_step(&mut self) -> Result<&mut Self, Error> { let new_branch = self.create_new_branch(self.tree.height())?; self.tree.val.simulate(match self.iteration_scaling { @@ -448,7 +428,7 @@ mod tests { } impl genetic_node::GeneticNode for TestState { - fn simulate(&mut self, iterations: u64) -> Result<(), String> { + fn simulate(&mut self, iterations: u64) -> Result<(), Error> { self.score += iterations as f64; Ok(()) } @@ -457,15 +437,15 @@ mod tests { self.score } - fn calculate_scores_and_trim(&mut self) -> Result<(), String> { + fn calculate_scores_and_trim(&mut self) -> Result<(), Error> { Ok(()) } - fn mutate(&mut self) -> Result<(), String> { + fn mutate(&mut self) -> Result<(), Error> { Ok(()) } - fn initialize() -> Result, String> { + fn initialize() -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } } diff --git a/gemla/src/error.rs b/gemla/src/error.rs new file mode 100644 index 0000000..95f8858 --- /dev/null +++ b/gemla/src/error.rs @@ -0,0 +1,18 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error(transparent)] + FileLinked(file_linked::Error), + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +impl From for Error { + fn from(error: file_linked::Error) -> Error { + match error { + file_linked::Error::Other(e) => Error::Other(e), + _ => Error::FileLinked(error), + } + } +} diff --git a/gemla/src/lib.rs b/gemla/src/lib.rs index 310ab4b..187d872 100644 --- a/gemla/src/lib.rs +++ b/gemla/src/lib.rs @@ -5,3 +5,4 @@ extern crate regex; pub mod tree; pub mod bracket; pub mod constants; +pub mod error; diff --git a/gemla/src/tree/mod.rs b/gemla/src/tree/mod.rs index 357f25a..7c4cf34 100644 --- a/gemla/src/tree/mod.rs +++ b/gemla/src/tree/mod.rs @@ -16,11 +16,9 @@ //! assert_eq!(t.right.unwrap().val, 3); //! ``` -use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::cmp::max; use std::fmt; -use std::str::FromStr; /// An unbalanced binary tree type where each node has an optional left and right child. /// @@ -155,17 +153,6 @@ impl fmt::Debug for Tree { } } -impl FromStr for Tree -where - T: FromStr + DeserializeOwned, -{ - type Err = String; - - fn from_str(s: &str) -> Result { - serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) - } -} - #[cfg(test)] mod tests { use super::*;