mod batch;
mod batch_verifier;
mod context;
mod factory;
mod handler;
mod message;
mod pending_batches;
pub use batch::Batch;
pub use batch_verifier::{BatchExecutionResult, BatchVerifier, BatchVerifierFactory};
pub use context::PublishingContext;
pub use factory::PublishFactory;
pub use handler::PublishHandle;
pub use pending_batches::PendingBatches;
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
use crate::artifact::{Artifact, ArtifactCreator, ArtifactCreatorFactory};
use crate::error::InternalError;
#[test]
fn test_artifact_publishing() {
let artifact_creator_factory = Box::new(TestArtifactCreatorFactory {});
let batch_verifier_factory = Box::new(TestBatchVerifierFactory {});
let mut publish_factory: PublishFactory<
TestBatch,
Arc<Mutex<TestContext>>,
TestArtifact,
TestBatchExecutionResult,
> = PublishFactory::new(artifact_creator_factory, batch_verifier_factory);
let pending_batches = Box::new(BatchIter {
batches: vec![TestBatch {
value: "value_1".to_string(),
}],
});
let context = Arc::new(Mutex::new(TestContext {
current_block_height: 0,
current_state_value: "genesis".to_string(),
}));
let publish_handle = publish_factory
.start(context.clone(), pending_batches)
.expect("Unable to start publishing thread");
publish_handle
.next_batch()
.expect("Unable to notify publisher thread of new batch");
let artifact = publish_handle
.finish()
.expect("Unable to finalize publishing thread");
assert_eq!(artifact.block_height, 1);
assert_eq!(artifact.current_value, "value_1".to_string());
let context_unlocked = context.lock().unwrap();
assert_eq!(context_unlocked.current_block_height, 1);
assert_eq!(context_unlocked.current_state_value, "value_1".to_string());
drop(context_unlocked);
let pending_batches = Box::new(BatchIter {
batches: vec![TestBatch {
value: "value_bad".to_string(),
}],
});
let publish_handle = publish_factory
.start(context.clone(), pending_batches)
.expect("Unable to start publishing thread");
publish_handle
.next_batch()
.expect("Unable to notify publisher thread of new batch");
publish_handle
.cancel()
.expect("Unable to finalize publishing thread");
let context_unlocked = context.lock().unwrap();
assert_eq!(context_unlocked.current_block_height, 1);
assert_eq!(context_unlocked.current_state_value, "value_1".to_string());
drop(context_unlocked);
let pending_batches = Box::new(BatchIter {
batches: vec![TestBatch {
value: "value_2".to_string(),
}],
});
let publish_handle = publish_factory
.start(context.clone(), pending_batches)
.expect("Unable to start publishing thread");
publish_handle
.next_batch()
.expect("Unable to notify publisher thread of new batch");
let artifact = publish_handle
.finish()
.expect("Unable to finalize publishing thread");
assert_eq!(artifact.block_height, 2);
assert_eq!(artifact.current_value, "value_2".to_string());
let context_unlocked = context.lock().unwrap();
assert_eq!(context_unlocked.current_block_height, 2);
assert_eq!(context_unlocked.current_state_value, "value_2".to_string());
}
#[derive(Clone)]
struct TestBatch {
value: String,
}
impl Batch for TestBatch {}
#[derive(Clone)]
struct TestContext {
current_block_height: i32,
current_state_value: String,
}
impl PublishingContext for Arc<Mutex<TestContext>> {}
#[derive(Clone)]
struct TestArtifact {
block_height: i32,
current_value: String,
}
impl Artifact for TestArtifact {
type Identifier = i32;
fn artifact_id(&self) -> &Self::Identifier {
&self.block_height
}
}
struct TestArtifactCreator {}
impl ArtifactCreator for TestArtifactCreator {
type Context = Arc<Mutex<TestContext>>;
type Input = Vec<TestBatchExecutionResult>;
type Artifact = TestArtifact;
fn create(
&self,
context: &mut Self::Context,
input: Self::Input,
) -> Result<Self::Artifact, InternalError> {
let mut context = context.lock().unwrap();
context.current_block_height = context.current_block_height + 1;
context.current_state_value = input[0].value.to_string();
Ok(TestArtifact {
block_height: context.current_block_height,
current_value: input[0].value.to_string(),
})
}
}
struct TestArtifactCreatorFactory {}
impl ArtifactCreatorFactory for TestArtifactCreatorFactory {
type ArtifactCreator = Box<
dyn ArtifactCreator<
Context = Arc<Mutex<TestContext>>,
Input = Vec<TestBatchExecutionResult>,
Artifact = TestArtifact,
>,
>;
fn new_creator(&self) -> Result<Self::ArtifactCreator, InternalError> {
Ok(Box::new(TestArtifactCreator {}))
}
}
struct TestBatchExecutionResult {
value: String,
}
impl BatchExecutionResult for TestBatchExecutionResult {}
struct TestBatchVerifier {
current_value: Option<String>,
}
impl BatchVerifier for TestBatchVerifier {
type Batch = TestBatch;
type Context = Arc<Mutex<TestContext>>;
type ExecutionResult = TestBatchExecutionResult;
fn add_batch(&mut self, batch: Self::Batch) -> Result<(), InternalError> {
self.current_value = Some(batch.value);
Ok(())
}
fn finalize(&mut self) -> Result<Vec<Self::ExecutionResult>, InternalError> {
Ok(vec![TestBatchExecutionResult {
value: self
.current_value
.as_ref()
.ok_or_else(|| InternalError::with_message("no value set".into()))?
.clone(),
}])
}
fn cancel(&mut self) -> Result<(), InternalError> {
self.current_value = None;
Ok(())
}
}
struct TestBatchVerifierFactory {}
impl BatchVerifierFactory for TestBatchVerifierFactory {
type Batch = TestBatch;
type Context = Arc<Mutex<TestContext>>;
type ExecutionResult = TestBatchExecutionResult;
#[allow(clippy::type_complexity)]
fn start(
&mut self,
_context: Self::Context,
) -> Result<
Box<
dyn BatchVerifier<
Batch = Self::Batch,
Context = Self::Context,
ExecutionResult = Self::ExecutionResult,
>,
>,
InternalError,
> {
Ok(Box::new(TestBatchVerifier {
current_value: None,
}))
}
}
struct BatchIter {
batches: Vec<TestBatch>,
}
impl PendingBatches<TestBatch> for BatchIter {
fn next(&mut self) -> Result<Option<TestBatch>, InternalError> {
Ok(self.batches.pop())
}
}
}