diff --git a/src/error/unexpected.rs b/src/error/unexpected.rs index fc5e082..ce31f35 100644 --- a/src/error/unexpected.rs +++ b/src/error/unexpected.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use std::{error, fmt}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] /// Error is a String which implements the Error trait. It is intended to be used in /// situations where the caller is being given an error they can't really handle, except to pass it /// along or log it. diff --git a/src/lib.rs b/src/lib.rs index 1e3d3e2..f3bb63f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(result_option_inspect)] #![feature(iterator_try_collect)] +#![feature(iter_collect_into)] pub mod domain; pub mod error; diff --git a/src/origin/store.rs b/src/origin/store.rs index f376656..a108d98 100644 --- a/src/origin/store.rs +++ b/src/origin/store.rs @@ -3,13 +3,14 @@ use crate::origin; use std::sync; pub mod git; +pub mod mux; #[derive(Clone, Copy)] pub struct Limits { // TODO storage limits } -#[derive(thiserror::Error, Debug)] +#[derive(thiserror::Error, Clone, Debug, PartialEq)] pub enum SyncError { #[error("invalid url")] InvalidURL, @@ -24,7 +25,7 @@ pub enum SyncError { Unexpected(#[from] unexpected::Error), } -#[derive(thiserror::Error, Debug)] +#[derive(thiserror::Error, Clone, Debug, PartialEq)] pub enum GetError { #[error("not found")] NotFound, @@ -33,7 +34,7 @@ pub enum GetError { Unexpected(#[from] unexpected::Error), } -#[derive(thiserror::Error, Debug)] +#[derive(thiserror::Error, Clone, Debug, PartialEq)] pub enum AllDescrsError { #[error(transparent)] Unexpected(#[from] unexpected::Error), diff --git a/src/origin/store/mux.rs b/src/origin/store/mux.rs new file mode 100644 index 0000000..b76be1a --- /dev/null +++ b/src/origin/store/mux.rs @@ -0,0 +1,94 @@ +use crate::error::unexpected::Mappable; +use crate::origin::{self, store}; +use std::sync; + +struct Store +where + S: store::Store, + F: Fn(&origin::Descr) -> Option + Sync + Send, +{ + mapping_fn: F, + //stores: Vec, +} + +pub fn new(mapping_fn: F) -> Box +where + S: store::Store + 'static, + F: Fn(&origin::Descr) -> Option + Sync + Send + 'static, +{ + Box::new(Store { mapping_fn }) +} + +impl store::Store for Store +where + S: store::Store, + F: Fn(&origin::Descr) -> Option + Sync + Send, +{ + fn sync(&self, descr: origin::Descr, limits: store::Limits) -> Result<(), store::SyncError> { + (self.mapping_fn)(&descr) + .or_unexpected_while(format!("fetching store for {:?}", &descr))? + .sync(descr, limits) + } + + fn get(&self, descr: origin::Descr) -> Result, store::GetError> { + (self.mapping_fn)(&descr) + .or_unexpected_while(format!("fetching store for {:?}", &descr))? + .get(descr) + } + + fn all_descrs(&self) -> Result, store::AllDescrsError> { + let mut res = Vec::::new(); + + //for store in self.stores.iter() { + // store.all_descrs()?.into_iter().collect_into(&mut res); + //} + + Ok(res) + } +} + +#[cfg(test)] +mod tests { + use crate::origin::{self, store}; + use mockall::predicate; + use std::{cell, rc}; + + #[test] + fn basic() { + let descrA = origin::Descr::Git { + url: "A".to_string(), + branch_name: "A".to_string(), + }; + + //let descrB = origin::Descr::Git { + // url: "B".to_string(), + // branch_name: "B".to_string(), + //}; + + let sA_mock = rc::Rc::new(store::MockStore::new()); + + //let mut sB = store::MockStore::new(); + + let s = { + let sA: rc::Rc = sA_mock.clone(); + let sA = sA.as_ref(); + + super::new( + |descr| match descr { + &origin::Descr::Git { ref url, .. } if url == "A" => Some(sA), + //&origin::Descr::Git { ref url, .. } if url == "B" => Some(sB), + _ => None, + }, + //vec![sA], + ) + }; + + sA_mock + .expect_sync() + .with(predicate::eq(descrA.clone()), predicate::always()) + .times(1) + .return_const(Ok::<(), store::SyncError>(())); + + assert_eq!(Ok(()), s.sync(descrA, store::Limits {})) + } +}