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]
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::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<T>
@ -95,9 +108,12 @@ where
/// # 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() {
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, 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);
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<FileLinked<T>, String> {
pub fn from_file(path: path::PathBuf) -> Result<FileLinked<T>, 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"))?;

View file

@ -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" }
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
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<Box<Self>, String> {
/// fn initialize() -> Result<Box<Self>, 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<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.
/// 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, 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::<Node>::new()?;
/// assert_eq!(wrapped_node.data.unwrap().get_fit_score(), 0.0);
/// # Ok(())
/// # }
/// ```
pub fn new() -> Result<Self, String> {
pub fn new() -> Result<Self, Error> {
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),
}
}

View file

@ -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<TestState, Self::Err> {
/// # 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, Error> {
/// # Ok(Box::new(TestState { score: 0.0 }))
/// # }
/// # }
@ -135,7 +128,7 @@ where
impl<T> Bracket<T>
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<Box<Self>, String> {
/// fn initialize() -> Result<Box<Self>, 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<FileLinked<Self>, String> {
FileLinked::new(
pub fn initialize(file_path: path::PathBuf) -> Result<FileLinked<Self>, 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<TestState, Self::Err> {
/// # 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, 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<tree::Tree<T>, String> {
fn create_new_branch(&self, height: u64) -> Result<tree::Tree<T>, 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<TestState, Self::Err> {
/// # 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<Box<Self>, String> {
/// # fn initialize() -> Result<Box<Self>, 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<Box<Self>, String> {
fn initialize() -> Result<Box<Self>, Error> {
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 bracket;
pub mod constants;
pub mod error;

View file

@ -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<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)]
mod tests {
use super::*;