Cleaning up code and adding more documentation
This commit is contained in:
parent
38d9ea7bce
commit
c6ae9b71cd
5 changed files with 134 additions and 13 deletions
|
@ -1,5 +1,11 @@
|
|||
//! Constants used in the `file_linked` crate.
|
||||
//! This module defines enums and constants that are used throughout the crate, such as data formats for serialization.
|
||||
|
||||
/// Data formats supported for serialization and deserialization.
|
||||
#[derive(Debug)]
|
||||
pub enum DataFormat {
|
||||
/// Use the `bincode` format for serialization.
|
||||
Bincode,
|
||||
/// Use the `json` format for serialization.
|
||||
Json,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
//! Error types for the `file_linked` crate.
|
||||
//! This module defines a unified error type for the crate, wrapping errors from dependencies and providing conversions
|
||||
//! from common error types.
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// The main error type for the `file_linked` crate.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// An error originating from the `bincode` crate.
|
||||
#[error(transparent)]
|
||||
Serialization(bincode::Error),
|
||||
|
||||
/// An IO error, such as file not found or permission denied.
|
||||
#[error(transparent)]
|
||||
IO(std::io::Error),
|
||||
|
||||
/// Any other error, wrapped using `anyhow`.
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,61 @@
|
|||
//! A wrapper around an object that ties it to a physical file
|
||||
//! # file_linked
|
||||
//!
|
||||
//! **file_linked** is a Rust library for binding objects directly to files, providing persistent, file-backed state management for any serializable type. It is designed for use cases where you want to transparently synchronize in-memory data with a file, supporting both synchronous and asynchronous mutation patterns.
|
||||
//!
|
||||
//! ## Features
|
||||
//! - Generic wrapper [`FileLinked<T>`] for any serializable type
|
||||
//! - Automatic file synchronization on mutation or replacement
|
||||
//! - Asynchronous and thread-safe access using `tokio::RwLock`
|
||||
//! - Support for multiple serialization formats (JSON, Bincode)
|
||||
//! - Simple API for reading, mutating, and replacing data
|
||||
//! - Error handling with unified [`Error`] type
|
||||
//!
|
||||
//! ## Modules
|
||||
//! - [`constants`]: Data format selection and related constants
|
||||
//! - [`error`]: Error types for serialization, I/O, and general errors
|
||||
//!
|
||||
//! ## Example
|
||||
//! ```rust,ignore
|
||||
//! # use file_linked::*;
|
||||
//! # use file_linked::constants::data_format::DataFormat;
|
||||
//! # use serde::{Deserialize, Serialize};
|
||||
//! # use std::path::PathBuf;
|
||||
//! # use tokio;
|
||||
//!
|
||||
//! #[derive(Deserialize, Serialize)]
|
||||
//! struct Test {
|
||||
//! pub a: u32,
|
||||
//! pub b: String,
|
||||
//! pub c: f64
|
||||
//! }
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() {
|
||||
//! let test = Test { a: 1, b: String::from("two"), c: 3.0 };
|
||||
//! let mut linked = FileLinked::new(test, &PathBuf::from("./file"), DataFormat::Json).await.unwrap();
|
||||
//! linked.mutate(|x| x.a += 1).await.unwrap();
|
||||
//! assert_eq!(linked.readonly().read().await.a, 2);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Usage
|
||||
//! 1. Create a serializable struct and instantiate a [`FileLinked`] object.
|
||||
//! 2. Use `.readonly()` for shared access, `.mutate()` or `.replace()` for mutation (which automatically syncs to file).
|
||||
//! 3. Use `.from_file()` to restore from disk.
|
||||
//!
|
||||
//! ## Crate Organization
|
||||
//! - All core logic is in [`FileLinked`].
|
||||
//! - Data format selection is in [`constants`].
|
||||
//! - Error types and helpers are in [`error`].
|
||||
//!
|
||||
//! [`FileLinked`]: crate::FileLinked
|
||||
//! [`Error`]: crate::error::Error
|
||||
//! [`constants`]: crate::constants
|
||||
//! [`error`]: crate::error
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
/// Constants used in the `file_linked` crate.
|
||||
pub mod constants;
|
||||
pub mod error;
|
||||
|
||||
|
@ -236,12 +292,14 @@ where
|
|||
/// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode).await
|
||||
/// .expect("Unable to create file linked object");
|
||||
///
|
||||
/// // Scope to limit the lifetime of the read lock
|
||||
/// {
|
||||
/// let readonly = linked_test.readonly();
|
||||
/// let readonly_ref = readonly.read().await;
|
||||
/// assert_eq!(readonly_ref.a, 1);
|
||||
/// }
|
||||
///
|
||||
/// // After mutate closure completes the object will be written to a file
|
||||
/// linked_test.mutate(|t| t.a = 2).await?;
|
||||
///
|
||||
/// let readonly = linked_test.readonly();
|
||||
|
@ -332,6 +390,52 @@ where
|
|||
T: Serialize + DeserializeOwned + Send + 'static,
|
||||
{
|
||||
/// Asynchronously modifies the data contained in a `FileLinked` object using an async callback `op`.
|
||||
/// The callback `op` is given an `Arc<RwLock<T>>` to the underlying data, allowing for concurrent access patterns.
|
||||
/// After the async operation completes the data is written to a file to synchronize the state.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use file_linked::*;
|
||||
/// # use file_linked::error::Error;
|
||||
/// # use file_linked::constants::data_format::DataFormat;
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// # use std::path::PathBuf;
|
||||
/// # use tokio;
|
||||
/// #
|
||||
/// # #[derive(Deserialize, Serialize)]
|
||||
/// # struct Test {
|
||||
/// # pub a: u32,
|
||||
/// # pub b: String,
|
||||
/// # pub c: f64
|
||||
/// # }
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Error> {
|
||||
/// let test = Test {
|
||||
/// a: 1,
|
||||
/// b: String::from(""),
|
||||
/// c: 0.0
|
||||
/// };
|
||||
/// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode).await
|
||||
/// .expect("Unable to create file linked object");
|
||||
///
|
||||
/// // Scope to limit the lifetime of the read lock
|
||||
/// {
|
||||
/// let readonly = linked_test.readonly();
|
||||
/// let readonly_ref = readonly.read().await;
|
||||
/// assert_eq!(readonly_ref.a, 1);
|
||||
/// }
|
||||
///
|
||||
/// // After mutate_async closure completes the object will be written to a file
|
||||
/// linked_test.mutate_async(|t| async move { t.write().await.a = 2 }).await?;
|
||||
///
|
||||
/// let readonly = linked_test.readonly();
|
||||
/// let readonly_ref = readonly.read().await;
|
||||
/// assert_eq!(readonly_ref.a, 2);
|
||||
/// # drop(linked_test);
|
||||
/// # std::fs::remove_file("./temp").expect("Unable to remove file");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn mutate_async<F, Fut, U>(&mut self, op: F) -> Result<U, Error>
|
||||
where
|
||||
F: FnOnce(Arc<RwLock<T>>) -> Fut,
|
||||
|
|
|
@ -7,13 +7,13 @@ mod test_state;
|
|||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use test_state::TestState;
|
||||
use file_linked::constants::data_format::DataFormat;
|
||||
use gemla::{
|
||||
core::{Gemla, GemlaConfig},
|
||||
error::log_error,
|
||||
};
|
||||
use std::{path::PathBuf, time::Instant};
|
||||
use test_state::TestState;
|
||||
|
||||
// const NUM_THREADS: usize = 2;
|
||||
|
||||
|
|
|
@ -62,13 +62,13 @@ pub struct GeneticNodeContext<S> {
|
|||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// [`Gemla`]: crate::Gemla
|
||||
#[async_trait]
|
||||
pub trait GeneticNode: Send {
|
||||
/// Custom type that provides a shared context across different nodes and simulations. Useful if you want to manage
|
||||
/// conncurrency or share data between nodes.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// pub struct SharedContext {
|
||||
|
@ -89,7 +89,7 @@ pub trait GeneticNode: Send {
|
|||
/// * A boxed instance of the initialized node.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use gemla::core::genetic_node::{GeneticNode, GeneticNodeContext};
|
||||
/// # use async_trait::async_trait;
|
||||
/// # use serde::{Serialize, Deserialize};
|
||||
|
@ -100,7 +100,7 @@ pub trait GeneticNode: Send {
|
|||
/// population: Vec<i64>,
|
||||
/// max_generations: u64,
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl GeneticNode for TestState {
|
||||
/// type Context = ();
|
||||
|
@ -125,7 +125,7 @@ pub trait GeneticNode: Send {
|
|||
/// * `Ok(true)` if the node should continue to the next phase, `Ok(false)` if finished.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use gemla::core::genetic_node::{GeneticNode, GeneticNodeContext};
|
||||
/// # use async_trait::async_trait;
|
||||
/// # use serde::{Serialize, Deserialize};
|
||||
|
@ -136,7 +136,7 @@ pub trait GeneticNode: Send {
|
|||
/// population: Vec<i64>,
|
||||
/// max_generations: u64,
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl GeneticNode for TestState {
|
||||
/// type Context = ();
|
||||
|
@ -152,7 +152,8 @@ pub trait GeneticNode: Send {
|
|||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
async fn simulate(&mut self, context: GeneticNodeContext<Self::Context>) -> Result<bool, Error>;
|
||||
async fn simulate(&mut self, context: GeneticNodeContext<Self::Context>)
|
||||
-> Result<bool, Error>;
|
||||
|
||||
/// Mutates members in a population and/or crossbreeds them to produce new offspring.
|
||||
///
|
||||
|
@ -164,7 +165,7 @@ pub trait GeneticNode: Send {
|
|||
/// * `Err(Error)` if an error occurred during mutation.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use gemla::core::genetic_node::{GeneticNode, GeneticNodeContext};
|
||||
/// # use async_trait::async_trait;
|
||||
/// # use serde::{Serialize, Deserialize};
|
||||
|
@ -175,7 +176,7 @@ pub trait GeneticNode: Send {
|
|||
/// population: Vec<i64>,
|
||||
/// max_generations: u64,
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl GeneticNode for TestState {
|
||||
/// type Context = ();
|
||||
|
@ -213,7 +214,7 @@ pub trait GeneticNode: Send {
|
|||
/// * `context` - The context for merging.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use gemla::core::genetic_node::{GeneticNode, GeneticNodeContext};
|
||||
/// # use async_trait::async_trait;
|
||||
/// # use serde::{Serialize, Deserialize};
|
||||
|
@ -223,7 +224,7 @@ pub trait GeneticNode: Send {
|
|||
/// population: Vec<i64>,
|
||||
/// max_generations: u64,
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl GeneticNode for TestState {
|
||||
/// type Context = ();
|
||||
|
|
Loading…
Add table
Reference in a new issue