Revising error types

This commit is contained in:
vandomej 2021-10-02 17:13:01 -07:00
parent d4af685500
commit edc2f67897
8 changed files with 153 additions and 132 deletions

View file

@ -15,4 +15,6 @@ categories = ["filesystem", "data-structures"]
[dependencies] [dependencies]
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
thiserror = "1.0"
anyhow = "1.0"

View file

@ -4,11 +4,24 @@ extern crate serde;
use std::fmt; use std::fmt;
use std::fs; use std::fs;
use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use std::path; use std::path;
use anyhow::{anyhow, Context};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; 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 /// A wrapper around an object `T` that ties the object to a physical file
pub struct FileLinked<T> pub struct FileLinked<T>
@ -95,9 +108,12 @@ where
/// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # std::fs::remove_file("./temp").expect("Unable to remove file");
/// # } /// # }
/// ``` /// ```
pub fn new(val: T, path: path::PathBuf) -> Result<FileLinked<T>, String> { pub fn new(val: T, path: path::PathBuf) -> Result<FileLinked<T>, Error> {
if path.is_file() { 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 }; let result = FileLinked { val, path };
@ -107,20 +123,20 @@ where
Ok(result) Ok(result)
} }
fn write_data(&self) -> Result<(), String> { fn write_data(&self) -> Result<(), Error> {
let mut file = fs::OpenOptions::new() let mut file = fs::OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
.truncate(true) .truncate(true)
.open(&self.path) .open(&self.path)
.map_err(|_| format!("Unable to open path {}", self.path.display()))?; .with_context(|| format!("Unable to open path {}", self.path.display()))?;
write!( write!(
file, 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(()) Ok(())
} }
@ -143,7 +159,7 @@ where
/// # pub c: f64 /// # pub c: f64
/// # } /// # }
/// # /// #
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let test = Test { /// let test = Test {
/// a: 1, /// a: 1,
/// b: String::from(""), /// b: String::from(""),
@ -164,7 +180,7 @@ where
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn mutate<U, F: FnOnce(&mut T) -> U>(&mut self, op: F) -> Result<U, String> { pub fn mutate<U, F: FnOnce(&mut T) -> U>(&mut self, op: F) -> Result<U, Error> {
let result = op(&mut self.val); let result = op(&mut self.val);
self.write_data()?; self.write_data()?;
@ -189,7 +205,7 @@ where
/// # pub c: f64 /// # pub c: f64
/// # } /// # }
/// # /// #
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let test = Test { /// let test = Test {
/// a: 1, /// a: 1,
/// b: String::from(""), /// b: String::from(""),
@ -214,7 +230,7 @@ where
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn replace(&mut self, val: T) -> Result<(), String> { pub fn replace(&mut self, val: T) -> Result<(), Error> {
self.val = val; self.val = val;
self.write_data() self.write_data()
@ -245,7 +261,7 @@ where
/// # pub c: f64 /// # pub c: f64
/// # } /// # }
/// # /// #
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let test = Test { /// let test = Test {
/// a: 1, /// a: 1,
/// b: String::from("2"), /// b: String::from("2"),
@ -278,27 +294,33 @@ where
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn from_file(path: path::PathBuf) -> Result<FileLinked<T>, String> { pub fn from_file(path: path::PathBuf) -> Result<FileLinked<T>, Error> {
if !path.is_file() { 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 let metadata = path
.metadata() .metadata()
.map_err(|_| format!("Error obtaining metadata for {}", path.display()))?; .with_context(|| format!("Error obtaining metadata for {}", path.display()))?;
if metadata.is_file() { if metadata.is_file() {
let file = fs::OpenOptions::new() let file = fs::OpenOptions::new()
.read(true) .read(true)
.open(&path) .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) 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 }) Ok(FileLinked { val, path })
} else { } 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; use std::fs;
#[test] #[test]
fn test_mutate() -> Result<(), String> { fn test_mutate() -> Result<(), Error> {
let list = vec![1, 2, 3, 4]; let list = vec![1, 2, 3, 4];
let mut file_linked_list = FileLinked::new(list, path::PathBuf::from("test.txt"))?; let mut file_linked_list = FileLinked::new(list, path::PathBuf::from("test.txt"))?;

View file

@ -18,4 +18,6 @@ uuid = { version = "0.7", features = ["serde", "v4"] }
clap = { version = "~2.27.0", features = ["yaml"] } clap = { version = "~2.27.0", features = ["yaml"] }
toml = "0.5.8" toml = "0.5.8"
regex = "1" regex = "1"
file_linked = { version = "0.1.0", path = "../file_linked" } file_linked = { version = "0.1.0", path = "../file_linked" }
thiserror = "1.0"
anyhow = "1.0"

View file

@ -2,6 +2,9 @@
//! //!
//! [`Bracket`]: crate::bracket::Bracket //! [`Bracket`]: crate::bracket::Bracket
use crate::error::Error;
use anyhow::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
@ -33,19 +36,20 @@ pub trait GeneticNode {
/// ///
/// ``` /// ```
/// # use gemla::bracket::genetic_node::GeneticNode; /// # use gemla::bracket::genetic_node::GeneticNode;
/// # use gemla::error::Error;
/// # /// #
/// struct Node { /// struct Node {
/// pub fit_score: f64, /// pub fit_score: f64,
/// } /// }
/// ///
/// impl GeneticNode for Node { /// impl GeneticNode for Node {
/// fn initialize() -> Result<Box<Self>, String> { /// fn initialize() -> Result<Box<Self>, Error> {
/// Ok(Box::new(Node {fit_score: 0.0})) /// 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(()) /// # Ok(())
/// # } /// # }
/// # /// #
@ -53,22 +57,22 @@ pub trait GeneticNode {
/// # self.fit_score /// # self.fit_score
/// # } /// # }
/// # /// #
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// } /// }
/// ///
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let node = Node::initialize()?; /// let node = Node::initialize()?;
/// assert_eq!(node.get_fit_score(), 0.0); /// assert_eq!(node.get_fit_score(), 0.0);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
fn initialize() -> Result<Box<Self>, String>; fn initialize() -> Result<Box<Self>, Error>;
/// Runs a simulation on the state object for the given number of `iterations` in order to guage it's fitness. /// 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. /// 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::bracket::genetic_node::GeneticNode;
/// # use gemla::error::Error;
/// # /// #
/// struct Model { /// struct Model {
/// pub fit_score: f64, /// pub fit_score: f64,
@ -89,7 +94,7 @@ pub trait GeneticNode {
/// } /// }
/// ///
/// impl Model { /// impl Model {
/// fn fit(&mut self, epochs: u64) -> Result<(), String> { /// fn fit(&mut self, epochs: u64) -> Result<(), Error> {
/// //... /// //...
/// # self.fit_score += epochs as f64; /// # self.fit_score += epochs as f64;
/// # Ok(()) /// # Ok(())
@ -97,13 +102,13 @@ pub trait GeneticNode {
/// } /// }
/// ///
/// impl GeneticNode for Node { /// impl GeneticNode for Node {
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(Node {models: vec![Model {fit_score: 0.0}]})) /// # 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() /// for m in self.models.iter_mut()
/// { /// {
/// m.fit(iterations)?; /// 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 /// # 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(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// } /// }
/// ///
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let mut node = Node::initialize()?; /// let mut node = Node::initialize()?;
/// node.simulate(5)?; /// node.simulate(5)?;
/// assert_eq!(node.get_fit_score(), 5.0); /// assert_eq!(node.get_fit_score(), 5.0);
/// # Ok(()) /// # 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. /// Returns a fit score associated with the nodes performance.
/// This will be used by a bracket in order to determine the most successful child. /// This will be used by a bracket in order to determine the most successful child.
@ -141,6 +146,7 @@ pub trait GeneticNode {
/// # Examples /// # Examples
/// ``` /// ```
/// # use gemla::bracket::genetic_node::GeneticNode; /// # use gemla::bracket::genetic_node::GeneticNode;
/// # use gemla::error::Error;
/// # /// #
/// struct Model { /// struct Model {
/// pub fit_score: f64, /// pub fit_score: f64,
@ -153,7 +159,7 @@ pub trait GeneticNode {
/// } /// }
/// ///
/// # impl Model { /// # impl Model {
/// # fn fit(&mut self, epochs: u64) -> Result<(), String> { /// # fn fit(&mut self, epochs: u64) -> Result<(), Error> {
/// # //... /// # //...
/// # self.fit_score += epochs as f64; /// # self.fit_score += epochs as f64;
/// # Ok(()) /// # Ok(())
@ -161,13 +167,13 @@ pub trait GeneticNode {
/// # } /// # }
/// ///
/// impl GeneticNode for Node { /// impl GeneticNode for Node {
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(Node {models: vec![Model {fit_score: 0.0}]})) /// # 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() /// # for m in self.models.iter_mut()
/// # { /// # {
/// # m.fit(iterations)?; /// # 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(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// } /// }
/// ///
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let mut node = Node::initialize()?; /// let mut node = Node::initialize()?;
/// node.simulate(5)?; /// node.simulate(5)?;
/// assert_eq!(node.get_fit_score(), 5.0); /// assert_eq!(node.get_fit_score(), 5.0);
@ -205,6 +211,7 @@ pub trait GeneticNode {
/// # Examples /// # Examples
/// ``` /// ```
/// # use gemla::bracket::genetic_node::GeneticNode; /// # use gemla::bracket::genetic_node::GeneticNode;
/// # use gemla::error::Error;
/// # /// #
/// struct Model { /// struct Model {
/// pub fit_score: f64, /// pub fit_score: f64,
@ -218,7 +225,7 @@ pub trait GeneticNode {
/// } /// }
/// ///
/// # impl Model { /// # impl Model {
/// # fn fit(&mut self, epochs: u64) -> Result<(), String> { /// # fn fit(&mut self, epochs: u64) -> Result<(), Error> {
/// # //... /// # //...
/// # self.fit_score += epochs as f64; /// # self.fit_score += epochs as f64;
/// # Ok(()) /// # Ok(())
@ -226,7 +233,7 @@ pub trait GeneticNode {
/// # } /// # }
/// ///
/// impl GeneticNode for Node { /// impl GeneticNode for Node {
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(Node { /// # Ok(Box::new(Node {
/// # models: vec![ /// # models: vec![
/// # Model { fit_score: 0.0 }, /// # 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() { /// # for m in self.models.iter_mut() {
/// # m.fit(iterations)?; /// # m.fit(iterations)?;
/// # } /// # }
@ -258,7 +265,7 @@ pub trait GeneticNode {
/// # .fit_score /// # .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.sort_by(|a, b| a.fit_score.partial_cmp(&b.fit_score).unwrap().reverse());
/// self.models.truncate(3); /// self.models.truncate(3);
/// Ok(()) /// Ok(())
@ -266,12 +273,12 @@ pub trait GeneticNode {
/// ///
/// //... /// //...
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// } /// }
/// ///
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let mut node = Node::initialize()?; /// let mut node = Node::initialize()?;
/// assert_eq!(node.models.len(), 5); /// assert_eq!(node.models.len(), 5);
/// ///
@ -283,13 +290,14 @@ pub trait GeneticNode {
/// # Ok(()) /// # 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. /// Mutates members in a population and/or crossbreeds them to produce new offspring.
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// # use gemla::bracket::genetic_node::GeneticNode; /// # use gemla::bracket::genetic_node::GeneticNode;
/// # use gemla::error::Error;
/// # use std::convert::TryInto; /// # use std::convert::TryInto;
/// # /// #
/// struct Model { /// struct Model {
@ -304,7 +312,7 @@ pub trait GeneticNode {
/// } /// }
/// ///
/// # impl Model { /// # impl Model {
/// # fn fit(&mut self, epochs: u64) -> Result<(), String> { /// # fn fit(&mut self, epochs: u64) -> Result<(), Error> {
/// # //... /// # //...
/// # self.fit_score += epochs as f64; /// # self.fit_score += epochs as f64;
/// # Ok(()) /// # Ok(())
@ -320,7 +328,7 @@ pub trait GeneticNode {
/// } /// }
/// ///
/// impl GeneticNode for Node { /// impl GeneticNode for Node {
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(Node { /// # Ok(Box::new(Node {
/// # models: vec![ /// # models: vec![
/// # Model { fit_score: 0.0 }, /// # 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() { /// # for m in self.models.iter_mut() {
/// # m.fit(iterations)?; /// # m.fit(iterations)?;
/// # } /// # }
@ -348,14 +356,14 @@ pub trait GeneticNode {
/// # .fit_score /// # .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.sort_by(|a, b| a.fit_score.partial_cmp(&b.fit_score).unwrap().reverse());
/// # self.models.truncate(3); /// # self.models.truncate(3);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// //... /// //...
/// ///
/// fn mutate(&mut self) -> Result<(), String> { /// fn mutate(&mut self) -> Result<(), Error> {
/// loop { /// loop {
/// if self.models.len() < self.population_size.try_into().unwrap() /// 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()?; /// let mut node = Node::initialize()?;
/// assert_eq!(node.models.len(), 5); /// assert_eq!(node.models.len(), 5);
/// ///
@ -383,7 +391,7 @@ pub trait GeneticNode {
/// # Ok(()) /// # 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 /// 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::GeneticNode;
/// # use gemla::bracket::genetic_node::GeneticNodeWrapper; /// # use gemla::bracket::genetic_node::GeneticNodeWrapper;
/// # use gemla::error::Error;
/// # #[derive(Debug)] /// # #[derive(Debug)]
/// struct Node { /// struct Node {
/// # pub fit_score: f64, /// # pub fit_score: f64,
@ -418,12 +427,12 @@ where
/// ///
/// impl GeneticNode for Node { /// impl GeneticNode for Node {
/// //... /// //...
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(Node {fit_score: 0.0})) /// # 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(()) /// # Ok(())
/// # } /// # }
/// # /// #
@ -431,22 +440,22 @@ where
/// # self.fit_score /// # self.fit_score
/// # } /// # }
/// # /// #
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// } /// }
/// ///
/// # fn main() -> Result<(), String> { /// # fn main() -> Result<(), Error> {
/// let mut wrapped_node = GeneticNodeWrapper::<Node>::new()?; /// let mut wrapped_node = GeneticNodeWrapper::<Node>::new()?;
/// assert_eq!(wrapped_node.data.unwrap().get_fit_score(), 0.0); /// assert_eq!(wrapped_node.data.unwrap().get_fit_score(), 0.0);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn new() -> Result<Self, String> { pub fn new() -> Result<Self, Error> {
let mut node = GeneticNodeWrapper { let mut node = GeneticNodeWrapper {
data: None, data: None,
state: GeneticState::Initialize, state: GeneticState::Initialize,
@ -476,14 +485,14 @@ where
/// [`simulate`]: crate::bracket::genetic_node::GeneticNode#tymethod.simulate /// [`simulate`]: crate::bracket::genetic_node::GeneticNode#tymethod.simulate
/// [`calculate_scores_and_trim`]: crate::bracket::genetic_node::GeneticNode#tymethod.calculate_scores_and_trim /// [`calculate_scores_and_trim`]: crate::bracket::genetic_node::GeneticNode#tymethod.calculate_scores_and_trim
/// [`mutate`]: crate::bracket::genetic_node::GeneticNode#tymethod.mutate /// [`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. // Looping through each state transition until the number of iterations have been reached.
loop { loop {
match (self.state, self.data.as_ref()) { match (self.state, &self.data) {
(GeneticState::Initialize, _) => { (GeneticState::Initialize, _) => {
self.iteration = 0; self.iteration = 0;
let new_data = let new_data = T::initialize()
T::initialize().map_err(|e| format!("Error initializing node: {}", e))?; .with_context(|| format!("Error initializing node {:?}", self))?;
self.data = Some(*new_data); self.data = Some(*new_data);
self.state = GeneticState::Simulate; self.state = GeneticState::Simulate;
} }
@ -492,7 +501,7 @@ where
.as_mut() .as_mut()
.unwrap() .unwrap()
.simulate(5) .simulate(5)
.map_err(|e| format!("Error simulating node: {}", e))?; .with_context(|| format!("Error simulating node: {:?}", self))?;
self.state = GeneticState::Score; self.state = GeneticState::Score;
} }
(GeneticState::Score, Some(_)) => { (GeneticState::Score, Some(_)) => {
@ -500,7 +509,7 @@ where
.as_mut() .as_mut()
.unwrap() .unwrap()
.calculate_scores_and_trim() .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 { self.state = if self.iteration == iterations {
GeneticState::Finish GeneticState::Finish
@ -513,13 +522,13 @@ where
.as_mut() .as_mut()
.unwrap() .unwrap()
.mutate() .mutate()
.map_err(|e| format!("Error mutating node: {}", e))?; .with_context(|| format!("Error mutating node: {:?}", self))?;
self.state = GeneticState::Simulate; self.state = GeneticState::Simulate;
} }
(GeneticState::Finish, Some(_)) => { (GeneticState::Finish, Some(_)) => {
break; break;
} }
_ => return Err(format!("Error processing node {:?}", self.data)), _ => panic!("Error processing node {:?}", self.data),
} }
} }

View file

@ -3,14 +3,14 @@
pub mod genetic_node; pub mod genetic_node;
use super::tree; use crate::error::Error;
use crate::tree;
use file_linked::FileLinked; use file_linked::FileLinked;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::path; 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 /// As the bracket tree increases in height, `IterationScaling` can be used to configure the number of iterations that
/// a node runs for. /// a node runs for.
@ -19,6 +19,7 @@ use std::str::FromStr;
/// ///
/// ``` /// ```
/// # use gemla::bracket::*; /// # use gemla::bracket::*;
/// # use gemla::error::Error;
/// # use serde::{Deserialize, Serialize}; /// # use serde::{Deserialize, Serialize};
/// # use std::fmt; /// # use std::fmt;
/// # use std::str::FromStr; /// # use std::str::FromStr;
@ -30,14 +31,6 @@ use std::str::FromStr;
/// # pub score: f64, /// # pub score: f64,
/// # } /// # }
/// # /// #
/// # impl FromStr for TestState {
/// # type Err = String;
/// #
/// # fn from_str(s: &str) -> Result<TestState, Self::Err> {
/// # serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
/// # }
/// # }
/// #
/// # impl TestState { /// # impl TestState {
/// # fn new(score: f64) -> TestState { /// # fn new(score: f64) -> TestState {
/// # TestState { score: score } /// # TestState { score: score }
@ -45,7 +38,7 @@ use std::str::FromStr;
/// # } /// # }
/// # /// #
/// # impl genetic_node::GeneticNode for TestState { /// # 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; /// # self.score += iterations as f64;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -54,15 +47,15 @@ use std::str::FromStr;
/// # self.score /// # self.score
/// # } /// # }
/// # /// #
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(TestState { score: 0.0 })) /// # Ok(Box::new(TestState { score: 0.0 }))
/// # } /// # }
/// # } /// # }
@ -135,7 +128,7 @@ where
impl<T> Bracket<T> impl<T> Bracket<T>
where 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` /// Initializes a bracket of type `T` storing the contents to `file_path`
/// ///
@ -143,6 +136,7 @@ where
/// ``` /// ```
/// # use gemla::bracket::*; /// # use gemla::bracket::*;
/// # use gemla::btree; /// # use gemla::btree;
/// # use gemla::error::Error;
/// # use serde::{Deserialize, Serialize}; /// # use serde::{Deserialize, Serialize};
/// # use std::fmt; /// # use std::fmt;
/// # use std::str::FromStr; /// # use std::str::FromStr;
@ -175,7 +169,7 @@ where
/// } /// }
/// ///
/// impl genetic_node::GeneticNode for TestState { /// 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; /// # self.score += iterations as f64;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -184,15 +178,15 @@ where
/// # self.score /// # self.score
/// # } /// # }
/// # /// #
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// fn initialize() -> Result<Box<Self>, String> { /// fn initialize() -> Result<Box<Self>, Error> {
/// Ok(Box::new(TestState { score: 0.0 })) /// Ok(Box::new(TestState { score: 0.0 }))
/// } /// }
/// ///
@ -212,14 +206,14 @@ where
/// std::fs::remove_file("./temp").expect("Unable to remove file"); /// std::fs::remove_file("./temp").expect("Unable to remove file");
/// # } /// # }
/// ``` /// ```
pub fn initialize(file_path: path::PathBuf) -> Result<FileLinked<Self>, String> { pub fn initialize(file_path: path::PathBuf) -> Result<FileLinked<Self>, Error> {
FileLinked::new( Ok(FileLinked::new(
Bracket { Bracket {
tree: btree!(*T::initialize()?), tree: btree!(*T::initialize()?),
iteration_scaling: IterationScaling::default(), iteration_scaling: IterationScaling::default(),
}, },
file_path, file_path,
) )?)
} }
/// Given a bracket object, configures it's [`IterationScaling`]. /// Given a bracket object, configures it's [`IterationScaling`].
@ -227,6 +221,7 @@ where
/// # Examples /// # Examples
/// ``` /// ```
/// # use gemla::bracket::*; /// # use gemla::bracket::*;
/// # use gemla::error::Error;
/// # use serde::{Deserialize, Serialize}; /// # use serde::{Deserialize, Serialize};
/// # use std::fmt; /// # use std::fmt;
/// # use std::str::FromStr; /// # use std::str::FromStr;
@ -238,14 +233,6 @@ where
/// # pub score: f64, /// # pub score: f64,
/// # } /// # }
/// # /// #
/// # impl FromStr for TestState {
/// # type Err = String;
/// #
/// # fn from_str(s: &str) -> Result<TestState, Self::Err> {
/// # serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
/// # }
/// # }
/// #
/// # impl fmt::Display for TestState { /// # impl fmt::Display for TestState {
/// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { /// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// # write!(f, "{}", self.score) /// # write!(f, "{}", self.score)
@ -259,7 +246,7 @@ where
/// # } /// # }
/// # /// #
/// # impl genetic_node::GeneticNode for TestState { /// # 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; /// # self.score += iterations as f64;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -268,15 +255,15 @@ where
/// # self.score /// # self.score
/// # } /// # }
/// # /// #
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(TestState { score: 0.0 })) /// # 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. // 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. // This additionally simulates and evaluates nodes in the branch as it is built.
fn create_new_branch(&self, height: u64) -> Result<tree::Tree<T>, String> { fn create_new_branch(&self, height: u64) -> Result<tree::Tree<T>, Error> {
if height == 1 { if height == 1 {
let mut base_node = btree!(*T::initialize()?); let mut base_node = btree!(*T::initialize()?);
@ -337,6 +324,7 @@ where
/// # Examples /// # Examples
/// ``` /// ```
/// # use gemla::bracket::*; /// # use gemla::bracket::*;
/// # use gemla::error::Error;
/// # use serde::{Deserialize, Serialize}; /// # use serde::{Deserialize, Serialize};
/// # use std::fmt; /// # use std::fmt;
/// # use std::str::FromStr; /// # use std::str::FromStr;
@ -348,14 +336,6 @@ where
/// # pub score: f64, /// # pub score: f64,
/// # } /// # }
/// # /// #
/// # impl FromStr for TestState {
/// # type Err = String;
/// #
/// # fn from_str(s: &str) -> Result<TestState, Self::Err> {
/// # serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
/// # }
/// # }
/// #
/// # impl fmt::Display for TestState { /// # impl fmt::Display for TestState {
/// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { /// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// # write!(f, "{}", self.score) /// # write!(f, "{}", self.score)
@ -369,7 +349,7 @@ where
/// # } /// # }
/// # /// #
/// # impl genetic_node::GeneticNode for TestState { /// # 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; /// # self.score += iterations as f64;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -378,15 +358,15 @@ where
/// # self.score /// # self.score
/// # } /// # }
/// # /// #
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> { /// # fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn mutate(&mut self) -> Result<(), String> { /// # fn mutate(&mut self) -> Result<(), Error> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # /// #
/// # fn initialize() -> Result<Box<Self>, String> { /// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(TestState { score: 0.0 })) /// # Ok(Box::new(TestState { score: 0.0 }))
/// # } /// # }
/// # } /// # }
@ -407,7 +387,7 @@ where
/// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # 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())?; let new_branch = self.create_new_branch(self.tree.height())?;
self.tree.val.simulate(match self.iteration_scaling { self.tree.val.simulate(match self.iteration_scaling {
@ -448,7 +428,7 @@ mod tests {
} }
impl genetic_node::GeneticNode for TestState { 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; self.score += iterations as f64;
Ok(()) Ok(())
} }
@ -457,15 +437,15 @@ mod tests {
self.score self.score
} }
fn calculate_scores_and_trim(&mut self) -> Result<(), String> { fn calculate_scores_and_trim(&mut self) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn mutate(&mut self) -> Result<(), String> { fn mutate(&mut self) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn initialize() -> Result<Box<Self>, String> { fn initialize() -> Result<Box<Self>, Error> {
Ok(Box::new(TestState { score: 0.0 })) Ok(Box::new(TestState { score: 0.0 }))
} }
} }

18
gemla/src/error.rs Normal file
View file

@ -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<file_linked::Error> for Error {
fn from(error: file_linked::Error) -> Error {
match error {
file_linked::Error::Other(e) => Error::Other(e),
_ => Error::FileLinked(error),
}
}
}

View file

@ -5,3 +5,4 @@ extern crate regex;
pub mod tree; pub mod tree;
pub mod bracket; pub mod bracket;
pub mod constants; pub mod constants;
pub mod error;

View file

@ -16,11 +16,9 @@
//! assert_eq!(t.right.unwrap().val, 3); //! assert_eq!(t.right.unwrap().val, 3);
//! ``` //! ```
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::max; use std::cmp::max;
use std::fmt; use std::fmt;
use std::str::FromStr;
/// An unbalanced binary tree type where each node has an optional left and right child. /// An unbalanced binary tree type where each node has an optional left and right child.
/// ///
@ -155,17 +153,6 @@ impl<T: fmt::Debug + Serialize> fmt::Debug for Tree<T> {
} }
} }
impl<T> FromStr for Tree<T>
where
T: FromStr + DeserializeOwned,
{
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;