From 5e201f121f846ea8b4154d6019c076012c9794b1 Mon Sep 17 00:00:00 2001 From: sttk Date: Sun, 5 Jan 2025 23:11:00 +0900 Subject: [PATCH] new: added DaxBaseImpl --- src/async_group.rs | 1 - src/dax/base.rs | 659 ++++++++++++++++++++++++++++++++++ src/dax/mod.rs | 872 +++++++++++++++++++++++++++++++++++++++++++++ src/dax_conn.rs | 507 -------------------------- src/dax_src.rs | 507 -------------------------- src/errors.rs | 6 + src/lib.rs | 87 ++++- 7 files changed, 1615 insertions(+), 1024 deletions(-) create mode 100644 src/dax/base.rs create mode 100644 src/dax/mod.rs delete mode 100644 src/dax_conn.rs delete mode 100644 src/dax_src.rs diff --git a/src/async_group.rs b/src/async_group.rs index 5a2969d..1ae97ef 100644 --- a/src/async_group.rs +++ b/src/async_group.rs @@ -204,7 +204,6 @@ mod tests_async_group { .unwrap() { errors::AsyncGroup::ThreadPanicked { message } => assert_eq!(message, "panic 1"), - _ => panic!(), } } } diff --git a/src/dax/base.rs b/src/dax/base.rs new file mode 100644 index 0000000..bfba0ff --- /dev/null +++ b/src/dax/base.rs @@ -0,0 +1,659 @@ +// Copyright (C) 2024 Takayuki Sato. All Rights Reserved. +// This program is free software under MIT License. +// See the file LICENSE in this distribution for more details. + +use std::any; + +use super::*; +use crate::async_group::AsyncGroupSync; +use crate::errors; +use crate::errs::Err; +use crate::{Dax, DaxConn, DaxSrc}; + +static mut GLOBAL_DAX_SRC_LIST: DaxSrcList = DaxSrcList::new(false); + +/// Registers a global `DaxSrc` with its name. +/// +/// By registering with this funciton, `DaxSrc` becomes able to create a `DaxConn` that can be +/// used within a transaction. +pub fn uses(name: &str, ds: S) { + unsafe { + GLOBAL_DAX_SRC_LIST.add(name.to_string(), ds); + } +} + +/// Makes all globally registered DaxSrc(s) usable. +/// +/// This function forbids adding more global `DaxSrc`(s), and calls each `setup` method of all +/// registered `DaxSrc`(s) +pub fn setup() -> Result<(), Err> { + unsafe { + GLOBAL_DAX_SRC_LIST.fix(); + + let err_map = GLOBAL_DAX_SRC_LIST.setup(); + if err_map.len() > 0 { + return Err(Err::new(errors::DaxSrc::FailToSetupGlobal { + errors: err_map, + })); + } + } + + Ok(()) +} + +/// Closes and frees each resource of registered global `DaxSrc`(s). +/// +// This function should always be called before an application ends. +pub fn close() { + unsafe { + GLOBAL_DAX_SRC_LIST.close(); + } +} + +/// Calls `setup` function, the argument function, and `close` function in order. +/// +/// If `setup` function or the argument fucntion fails, this fucntion stops calling other functions +/// and return an `Err` containing the error reason. +pub fn start_app(app: fn() -> Result<(), Err>) -> Result<(), Err> { + if let Err(err) = setup() { + return Err(err); + } + + if let Err(err) = app() { + return Err(err); + } + + close(); + + Ok(()) +} + +/// Provides the methods to manage `DaxSrc`(s) and to operate `DaxConn`(s) in a transaction. +pub struct DaxBaseImpl { + local_dax_src_list: DaxSrcList, + dax_src_map: HashMap, + dax_conn_map: DaxConnMap, + fixed: bool, +} + +impl DaxBaseImpl { + /// Creates a `DaxBase` instance. + pub fn new() -> Self { + unsafe { + GLOBAL_DAX_SRC_LIST.fix(); + } + + let mut map = HashMap::::new(); + + let mut ptr = unsafe { GLOBAL_DAX_SRC_LIST.head }; + while !ptr.is_null() { + let next = unsafe { (*ptr).next }; + let name = unsafe { &(*ptr).name }; + map.insert(name.to_string(), ptr); + ptr = next; + } + + Self { + local_dax_src_list: DaxSrcList::new(true), + dax_src_map: map, + dax_conn_map: DaxConnMap::new(), + fixed: false, + } + } + + /// Adds a `DaxSrc` with a registration name into this `DaxBase` and setup it. + pub fn uses(&mut self, name: &str, ds: S) -> Result<(), Err> { + if self.fixed { + return Ok(()); + } + + self.local_dax_src_list.add(name.to_string(), ds); + + let ptr = self.local_dax_src_list.last; + let name = unsafe { &(*ptr).name }; + self.dax_src_map.insert(name.to_string(), ptr); + + let setup_fn = unsafe { (*ptr).setup_fn }; + let mut ag = AsyncGroupSync::new(); + if let Err(err) = setup_fn(ptr, &mut ag) { + return Err(err); + } + if let Some(err) = ag.err { + return Err(err); + } + + Ok(()) + } + + /// Removes a `DaxSrc` from this `DaxBase` and close it. + pub fn disuses(&mut self, name: &str) { + if self.fixed { + return; + } + + if let Some(ptr) = self.dax_src_map.remove(name) { + let close_fn = unsafe { (*ptr).close_fn }; + let drop_fn = unsafe { (*ptr).drop_fn }; + + if unsafe { (*ptr).local } { + let prev = unsafe { (*ptr).prev }; + let next = unsafe { (*ptr).next }; + if prev.is_null() && next.is_null() { + self.local_dax_src_list.head = ptr::null_mut(); + self.local_dax_src_list.last = ptr::null_mut(); + } else if prev.is_null() { + unsafe { (*next).prev = ptr::null_mut() }; + self.local_dax_src_list.head = next; + } else if next.is_null() { + unsafe { (*prev).next = ptr::null_mut() }; + self.local_dax_src_list.last = prev; + } else { + unsafe { (*next).prev = prev }; + unsafe { (*prev).next = next }; + } + } + + let mut ag = AsyncGroupSync::new(); + close_fn(ptr, &mut ag); + drop_fn(ptr); + } + } + + /// Closes all `DaxSrc`(s) in this `DaxBase`. + pub fn close(&mut self) { + if self.fixed { + return; + } + + self.local_dax_src_list.close(); + } + + /// Begins a transaction. + /// + /// This method prevents to add new `DaxSrc` into this `DaxBase`. + pub fn begin(&mut self) { + self.fixed = true; + } + + /// Commits all `DaxConn`(s) created in this `DaxBase`. + pub fn commit(&mut self) -> Result<(), Err> { + self.dax_conn_map.commit() + } + + /// Rollbacks all `DaxConn`(s) created in this `DaxBase`. + pub fn rollback(&mut self) { + self.dax_conn_map.rollback(); + } + + /// Ends a transaction. + /// + /// This method closes all `DaxConn`(s) created in this `DaxBase` and re-allow to add a new + /// `DaxSrc`. + pub fn end(&mut self) { + self.dax_conn_map.close(); + self.fixed = false; + } +} + +impl Drop for DaxBaseImpl { + fn drop(&mut self) { + self.close(); + } +} + +impl Dax for DaxBaseImpl { + fn get_dax_conn(&mut self, name: &str) -> Result<&C, Err> { + if let Some(ptr) = self.dax_conn_map.map.get(name) { + let type_id = any::TypeId::of::(); + let is_fn = unsafe { (*(*ptr)).is_fn }; + if is_fn(type_id) { + let typed_ptr = (*ptr) as *const DaxConnContainer; + return Ok(unsafe { &((*typed_ptr).dax_conn) }); + } + + return Err(Err::new(errors::DaxBase::FailToCastDaxConn { + name: name.to_string(), + to_type: any::type_name::(), + })); + } + + if let Some(ptr) = self.dax_src_map.get_mut(name) { + let create_dax_conn = unsafe { (*(*ptr)).create_dax_conn_fn }; + return match create_dax_conn(*ptr) { + Ok(dax_conn) => { + let ptr = Box::into_raw(dax_conn); + let typed_ptr = ptr as *mut C; + let boxed = unsafe { Box::from_raw(typed_ptr) }; + self.dax_conn_map.insert(name, boxed); + if let Some(ptr) = self.dax_conn_map.map.get(name) { + let typed_ptr = (*ptr) as *const DaxConnContainer; + return Ok(unsafe { &((*typed_ptr).dax_conn) }); + } + return Err(Err::new(errors::DaxBase::FailToGetDaxConn { + name: name.to_string(), + to_type: any::type_name::(), + })); + } + Err(err) => Err(err), + }; + } + + Err(Err::new(errors::DaxBase::FailToGetDaxConn { + name: name.to_string(), + to_type: any::type_name::(), + })) + } +} + +#[cfg(test)] +mod tests_dax_base_impl { + use super::*; + + use std::sync::{LazyLock, Mutex}; + + struct Logger { + log_vec: Vec, + } + + impl Logger { + fn new() -> Self { + Self { + log_vec: Vec::::new(), + } + } + fn log(&mut self, s: &str) { + self.log_vec.push(s.to_string()); + } + fn assert_logs(&self, logs: &[&str]) { + assert_eq!(self.log_vec.len(), logs.len()); + for i in 0..self.log_vec.len() { + assert_eq!(self.log_vec[i], logs[i]); + } + } + fn assert_log(&self, index: usize, log: &str) { + assert!(self.log_vec.len() > index); + assert_eq!(self.log_vec[index], log); + } + fn assert_log_either(&self, index: usize, candidates: &[&str]) { + assert!(self.log_vec.len() > index); + assert!(candidates.contains(&&self.log_vec[index].as_str())); + } + fn clear(&mut self) { + self.log_vec.clear(); + } + } + + static LOGGER: LazyLock> = LazyLock::new(|| Mutex::new(Logger::new())); + + struct FooDaxConn { + committed: bool, + } + + impl FooDaxConn { + fn new() -> Self { + LOGGER.lock().unwrap().log("create FooDaxConn"); + Self { committed: false } + } + + fn get_bool(&self) -> bool { + true + } + } + + impl DaxConn for FooDaxConn { + fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + self.committed = true; + LOGGER.lock().unwrap().log("commit FooDaxConn"); + Ok(()) + } + fn is_committed(&self) -> bool { + self.committed + } + fn rollback(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("rollback FooDaxConn"); + } + fn force_back(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("force back FooDaxConn"); + } + fn close(&mut self) { + LOGGER.lock().unwrap().log("close FooDaxConn"); + } + } + + struct FooDaxSrc {} + + impl FooDaxSrc { + fn new() -> Self { + LOGGER.lock().unwrap().log("create FooDaxSrc"); + Self {} + } + } + + impl DaxSrc for FooDaxSrc { + fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + LOGGER.lock().unwrap().log("setup FooDaxSrc"); + Ok(()) + } + fn close(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("close FooDaxSrc"); + } + fn create_dax_conn(&mut self) -> Result, Err> { + LOGGER.lock().unwrap().log("create FooDaxConn of FooDaxSrc"); + Ok(Box::new(FooDaxConn::new())) + } + } + + struct BarDaxConn { + committed: bool, + } + + impl BarDaxConn { + fn new() -> Self { + LOGGER.lock().unwrap().log("create BarDaxConn"); + Self { committed: false } + } + + fn get_num(&self) -> u64 { + 123 + } + } + + impl DaxConn for BarDaxConn { + fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + self.committed = true; + LOGGER.lock().unwrap().log("commit BarDaxConn"); + Ok(()) + } + fn is_committed(&self) -> bool { + self.committed + } + fn rollback(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("rollback BarDaxConn"); + } + fn force_back(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("force back BarDaxConn"); + } + fn close(&mut self) { + LOGGER.lock().unwrap().log("close BarDaxConn"); + } + } + + struct BarDaxSrc {} + + impl BarDaxSrc { + fn new() -> Self { + LOGGER.lock().unwrap().log("create BarDaxSrc"); + Self {} + } + } + + impl DaxSrc for BarDaxSrc { + fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + LOGGER.lock().unwrap().log("setup BarDaxSrc"); + Ok(()) + } + fn close(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("close BarDaxSrc"); + } + fn create_dax_conn(&mut self) -> Result, Err> { + LOGGER.lock().unwrap().log("create BarDaxConn of BarDaxSrc"); + Ok(Box::new(BarDaxConn::new())) + } + } + + fn test_dax_base_with_no_dax_conn() { + let mut base = DaxBaseImpl::new(); + + let _ = base.uses("bar", BarDaxSrc::new()); + + base.begin(); + base.commit().unwrap(); + base.end(); + } + + fn test_dax_base_with_dax_conns() { + let mut base = DaxBaseImpl::new(); + + let _ = base.uses("bar", BarDaxSrc::new()); + + base.begin(); + + let foo_conn: &FooDaxConn = base.get_dax_conn::("foo").unwrap(); + assert_eq!(foo_conn.get_bool(), true); + + let bar_conn: &BarDaxConn = base.get_dax_conn::("bar").unwrap(); + assert_eq!(bar_conn.get_num(), 123); + + base.commit().unwrap(); + base.end(); + } + + #[test] + fn test() { + uses("foo", FooDaxSrc::new()); + let _ = setup().unwrap(); + + test_dax_base_with_no_dax_conn(); + + LOGGER.lock().unwrap().assert_logs(&[ + "create FooDaxSrc", + "setup FooDaxSrc", + "create BarDaxSrc", + "setup BarDaxSrc", + "close BarDaxSrc", + ]); + + LOGGER.lock().unwrap().clear(); + + test_dax_base_with_dax_conns(); + + close(); + + LOGGER.lock().unwrap().assert_logs(&[ + "create BarDaxSrc", + "setup BarDaxSrc", + "create FooDaxConn of FooDaxSrc", + "create FooDaxConn", + "create BarDaxConn of BarDaxSrc", + "create BarDaxConn", + "commit FooDaxConn", + "commit BarDaxConn", + "close BarDaxConn", + "close FooDaxConn", + "close BarDaxSrc", + "close FooDaxSrc", + ]); + } +} + +#[cfg(test)] +mod tests_of_dax { + use super::*; + + struct FooDaxConn { + committed: bool, + } + impl FooDaxConn { + fn new() -> Self { + Self { committed: false } + } + fn get_string(&self) -> Result { + Ok(String::from("aaa")) + } + fn get_bool(&self) -> bool { + true + } + } + impl DaxConn for FooDaxConn { + fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + self.committed = true; + Ok(()) + } + fn is_committed(&self) -> bool { + self.committed + } + fn rollback(&mut self, _ag: &mut dyn AsyncGroup) {} + fn force_back(&mut self, _ag: &mut dyn AsyncGroup) {} + fn close(&mut self) {} + } + + struct FooDaxSrc {} + impl FooDaxSrc { + fn new() -> Self { + Self {} + } + } + impl DaxSrc for FooDaxSrc { + fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + Ok(()) + } + fn close(&mut self, _ag: &mut dyn AsyncGroup) {} + fn create_dax_conn(&mut self) -> Result, Err> { + Ok(Box::new(FooDaxConn::new())) + } + } + + struct BarDaxConn { + committed: bool, + } + impl BarDaxConn { + fn new() -> Self { + Self { committed: false } + } + fn get_num(&self) -> i64 { + -123 + } + } + impl DaxConn for BarDaxConn { + fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + self.committed = true; + Ok(()) + } + fn is_committed(&self) -> bool { + self.committed + } + fn rollback(&mut self, _ag: &mut dyn AsyncGroup) {} + fn force_back(&mut self, _ag: &mut dyn AsyncGroup) {} + fn close(&mut self) {} + } + + struct BarDaxSrc {} + impl BarDaxSrc { + fn new() -> Self { + Self {} + } + } + impl DaxSrc for BarDaxSrc { + fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + Ok(()) + } + fn close(&mut self, _ag: &mut dyn AsyncGroup) {} + fn create_dax_conn(&mut self) -> Result, Err> { + Ok(Box::new(BarDaxConn::new())) + } + } + + trait FooDax: Dax { + fn m01(&mut self) -> Result { + let conn = self.get_dax_conn::("foo").unwrap(); + conn.get_string() + } + fn m03(&mut self) -> Result { + let conn = self.get_dax_conn::("foo").unwrap(); + Ok(conn.get_bool()) + } + } + + trait BarDax: Dax { + fn m02(&mut self) -> Result { + let conn = self.get_dax_conn::("bar").unwrap(); + Ok(conn.get_num()) + } + } + + trait HogeLogicDax { + fn m01(&mut self) -> Result; + fn m02(&mut self) -> Result; + } + + trait FugaLogicDax { + fn m02(&mut self) -> Result; + fn m03(&mut self) -> Result; + } + + struct DaxBase { + pub(crate) Impl: DaxBaseImpl, + } + impl DaxBase { + fn new() -> Self { + Self { + Impl: DaxBaseImpl::new(), + } + } + fn uses(&mut self, name: &str, ds: S) -> Result<(), Err> { + self.Impl.uses(name, ds) + } + fn disuses(&mut self, name: &str) { + self.Impl.disuses(name); + } + } + impl Dax for DaxBase { + fn get_dax_conn(&mut self, name: &str) -> Result<&C, Err> { + self.Impl.get_dax_conn::(name) + } + } + impl FooDax for DaxBase {} + impl BarDax for DaxBase {} + impl HogeLogicDax for DaxBase { + fn m01(&mut self) -> Result { + FooDax::m01(self) + } + fn m02(&mut self) -> Result { + BarDax::m02(self) + } + } + impl FugaLogicDax for DaxBase { + fn m02(&mut self) -> Result { + BarDax::m02(self) + } + fn m03(&mut self) -> Result { + FooDax::m03(self) + } + } + + fn hoge_logic(dax: &mut dyn HogeLogicDax) -> Result<(), Err> { + let s = dax.m01()?; + assert_eq!(s, "aaa"); + + let n = dax.m02()?; + assert_eq!(n, -123); + + Ok(()) + } + + fn fuga_logic(dax: &mut dyn FugaLogicDax) -> Result<(), Err> { + let n = dax.m02()?; + assert_eq!(n, -123); + + let b = dax.m03()?; + assert_eq!(b, true); + + Ok(()) + } + + #[test] + fn test() { + let mut dax = DaxBase::new(); + + let _ = dax.uses("foo", FooDaxSrc::new()).unwrap(); + let _ = dax.uses("bar", BarDaxSrc::new()).unwrap(); + + hoge_logic(&mut dax); + let _ = dax.Impl.commit().unwrap(); + dax.Impl.end(); + + fuga_logic(&mut dax); + let _ = dax.Impl.commit().unwrap(); + dax.Impl.end(); + } +} diff --git a/src/dax/mod.rs b/src/dax/mod.rs new file mode 100644 index 0000000..85498c4 --- /dev/null +++ b/src/dax/mod.rs @@ -0,0 +1,872 @@ +// Copyright (C) 2024 Takayuki Sato. All Rights Reserved. +// This program is free software under MIT License. +// See the file LICENSE in this distribution for more details. + +mod base; + +pub use base::DaxBaseImpl; +pub use base::{close, setup, start_app, uses}; + +use std::any; +use std::collections::HashMap; +use std::ptr; + +use crate::async_group::{AsyncGroup, AsyncGroupAsync}; +use crate::errors; +use crate::errs::Err; +use crate::{DaxConn, DaxSrc, NoopDaxConn, NoopDaxSrc}; + +#[repr(C)] +struct DaxSrcContainer +where + S: DaxSrc + 'static, +{ + drop_fn: fn(*const DaxSrcContainer), + setup_fn: fn(*const DaxSrcContainer, ag: &mut dyn AsyncGroup) -> Result<(), Err>, + close_fn: fn(*const DaxSrcContainer, ag: &mut dyn AsyncGroup), + create_dax_conn_fn: fn(*const DaxSrcContainer) -> Result, Err>, + prev: *mut DaxSrcContainer, + next: *mut DaxSrcContainer, + local: bool, + name: String, + dax_src: S, +} + +impl DaxSrcContainer +where + S: DaxSrc + 'static, +{ + fn new(local: bool, name: String, dax_src: S) -> Self { + Self { + drop_fn: drop_dax_src::, + setup_fn: setup_dax_src::, + close_fn: close_dax_src::, + create_dax_conn_fn: create_dax_conn_from_dax_src::, + prev: ptr::null_mut(), + next: ptr::null_mut(), + local, + name, + dax_src, + } + } +} + +fn drop_dax_src(ptr: *const DaxSrcContainer) +where + S: DaxSrc + 'static, +{ + let typed_ptr = ptr as *mut DaxSrcContainer; + drop(unsafe { Box::from_raw(typed_ptr) }); +} + +fn setup_dax_src(ptr: *const DaxSrcContainer, ag: &mut dyn AsyncGroup) -> Result<(), Err> +where + S: DaxSrc + 'static, +{ + let typed_ptr = ptr as *mut DaxSrcContainer; + unsafe { (*typed_ptr).dax_src.setup(ag) } +} + +fn close_dax_src(ptr: *const DaxSrcContainer, ag: &mut dyn AsyncGroup) +where + S: DaxSrc + 'static, +{ + let typed_ptr = ptr as *mut DaxSrcContainer; + unsafe { (*typed_ptr).dax_src.close(ag) }; +} + +fn create_dax_conn_from_dax_src(ptr: *const DaxSrcContainer) -> Result, Err> +where + S: DaxSrc + 'static, +{ + let typed_ptr = ptr as *mut DaxSrcContainer; + unsafe { (*typed_ptr).dax_src.create_dax_conn() } +} + +#[repr(C)] +struct DaxConnContainer +where + C: DaxConn + 'static, +{ + drop_fn: fn(*const DaxConnContainer), + is_fn: fn(any::TypeId) -> bool, + commit_fn: fn(*const DaxConnContainer, &mut dyn AsyncGroup) -> Result<(), Err>, + is_committed_fn: fn(*const DaxConnContainer) -> bool, + rollback_fn: fn(*const DaxConnContainer, &mut dyn AsyncGroup), + force_back_fn: fn(*const DaxConnContainer, &mut dyn AsyncGroup), + close_fn: fn(*const DaxConnContainer), + prev: *mut DaxConnContainer, + next: *mut DaxConnContainer, + name: String, + dax_conn: Box, +} + +impl DaxConnContainer +where + C: DaxConn + 'static, +{ + fn new(name: &str, dax_conn: Box) -> Self { + Self { + drop_fn: drop_dax_conn::, + is_fn: is_dax_conn::, + commit_fn: commit_dax_conn::, + is_committed_fn: is_committed_dax_conn::, + rollback_fn: rollback_dax_conn::, + force_back_fn: force_back_dax_conn::, + close_fn: close_dax_conn::, + prev: ptr::null_mut(), + next: ptr::null_mut(), + name: name.to_string(), + dax_conn, + } + } +} + +fn drop_dax_conn(ptr: *const DaxConnContainer) +where + C: DaxConn + 'static, +{ + let typed_ptr = ptr as *mut DaxConnContainer; + unsafe { + drop(Box::from_raw(typed_ptr)); + } +} + +fn is_dax_conn(type_id: any::TypeId) -> bool +where + C: DaxConn + 'static, +{ + any::TypeId::of::() == type_id +} + +fn commit_dax_conn(ptr: *const DaxConnContainer, ag: &mut dyn AsyncGroup) -> Result<(), Err> +where + C: DaxConn + 'static, +{ + let typed_ptr = ptr as *mut DaxConnContainer; + unsafe { (*typed_ptr).dax_conn.commit(ag) } +} + +fn is_committed_dax_conn(ptr: *const DaxConnContainer) -> bool +where + C: DaxConn + 'static, +{ + let typed_ptr = ptr as *mut DaxConnContainer; + unsafe { (*typed_ptr).dax_conn.is_committed() } +} + +fn rollback_dax_conn(ptr: *const DaxConnContainer, ag: &mut dyn AsyncGroup) +where + C: DaxConn + 'static, +{ + let typed_ptr = ptr as *mut DaxConnContainer; + unsafe { (*typed_ptr).dax_conn.rollback(ag) } +} + +fn force_back_dax_conn(ptr: *const DaxConnContainer, ag: &mut dyn AsyncGroup) +where + C: DaxConn + 'static, +{ + let typed_ptr = ptr as *mut DaxConnContainer; + unsafe { (*typed_ptr).dax_conn.force_back(ag) }; +} + +fn close_dax_conn(ptr: *const DaxConnContainer) +where + C: DaxConn + 'static, +{ + let typed_ptr = ptr as *mut DaxConnContainer; + unsafe { (*typed_ptr).dax_conn.close() }; +} + +struct DaxSrcList { + head: *mut DaxSrcContainer, + last: *mut DaxSrcContainer, + fixed: bool, + local: bool, +} + +impl DaxSrcList { + const fn new(local: bool) -> Self { + Self { + head: ptr::null_mut(), + last: ptr::null_mut(), + fixed: false, + local, + } + } + + fn add(&mut self, name: String, ds: S) + where + S: DaxSrc + 'static, + { + if self.fixed { + return; + } + + let boxed = Box::new(DaxSrcContainer::::new(self.local, name, ds)); + let typed_ptr = Box::into_raw(boxed); + let ptr = typed_ptr.cast::(); + if self.last.is_null() { + self.head = ptr; + self.last = ptr; + } else { + unsafe { + (*self.last).next = ptr; + (*typed_ptr).prev = self.last; + } + self.last = ptr; + } + } + + fn fix(&mut self) { + self.fixed = true; + } + + fn setup(&mut self) -> HashMap { + let mut err_map = HashMap::new(); + + if self.head.is_null() { + return err_map; + } + + let mut ag = AsyncGroupAsync::new(); + + let mut ptr = self.head; + while !ptr.is_null() { + let setup_fn = unsafe { (*ptr).setup_fn }; + let next = unsafe { (*ptr).next }; + ag.name = unsafe { &(*ptr).name }; + if let Err(err) = setup_fn(ptr, &mut ag) { + err_map.insert(ag.name.to_string(), err); + } + ptr = next; + } + + ag.wait(&mut err_map); + + err_map + } + + fn close(&mut self) { + let mut err_map = HashMap::new(); + + if self.head.is_null() { + return; + } + + let mut ag = AsyncGroupAsync::new(); + + let mut ptr = self.last; + while !ptr.is_null() { + let close_fn = unsafe { (*ptr).close_fn }; + let prev = unsafe { (*ptr).prev }; + close_fn(ptr, &mut ag); + ptr = prev; + } + + ag.wait(&mut err_map); + } +} + +impl Drop for DaxSrcList { + fn drop(&mut self) { + let mut ptr = self.last; + while !ptr.is_null() { + let drop_fn = unsafe { (*ptr).drop_fn }; + let prev = unsafe { (*ptr).prev }; + drop_fn(ptr); + ptr = prev; + } + } +} + +struct DaxConnMap { + head: *mut DaxConnContainer, + last: *mut DaxConnContainer, + map: HashMap, +} + +impl DaxConnMap { + fn new() -> Self { + Self { + head: ptr::null_mut(), + last: ptr::null_mut(), + map: HashMap::new(), + } + } + + fn insert(&mut self, name: &str, conn: Box) + where + C: DaxConn + 'static, + { + let boxed = Box::new(DaxConnContainer::::new(name, conn)); + let typed_ptr = Box::into_raw(boxed); + let ptr = typed_ptr.cast::(); + if self.last.is_null() { + self.head = ptr; + self.last = ptr; + } else { + unsafe { + (*self.last).next = ptr; + (*typed_ptr).prev = self.last; + } + self.last = ptr; + } + + self.map.insert(name.to_string(), ptr); + } + + fn commit(&self) -> Result<(), Err> { + if self.last.is_null() { + return Ok(()); + } + + let mut err_map = HashMap::::new(); + let mut ag = AsyncGroupAsync::new(); + + let mut ptr = self.head; + while !ptr.is_null() { + let commit_fn = unsafe { (*ptr).commit_fn }; + let name = unsafe { &(*ptr).name }; + let next = unsafe { (*ptr).next }; + if let Err(err) = commit_fn(ptr, &mut ag) { + err_map.insert(name.to_string(), err); + } + ptr = next; + } + + ag.wait(&mut err_map); + + if err_map.is_empty() { + return Ok(()); + } + + Err(Err::new(errors::DaxConn::FailToCommit { errors: err_map })) + } + + fn rollback(&self) { + if self.last.is_null() { + return; + } + + let mut ag = AsyncGroupAsync::new(); + + let mut ptr = self.head; + while !ptr.is_null() { + let is_committed_fn = unsafe { (*ptr).is_committed_fn }; + let rollback_fn = unsafe { (*ptr).rollback_fn }; + let force_back_fn = unsafe { (*ptr).force_back_fn }; + let next = unsafe { (*ptr).next }; + if is_committed_fn(ptr) { + force_back_fn(ptr, &mut ag); + } else { + rollback_fn(ptr, &mut ag); + } + ptr = next; + } + + let mut err_map = HashMap::::new(); + ag.wait(&mut err_map); + } + + fn close(&self) { + if self.last.is_null() { + return; + } + + let mut ptr = self.last; + while !ptr.is_null() { + let close_fn = unsafe { (*ptr).close_fn }; + let prev = unsafe { (*ptr).prev }; + close_fn(ptr); + ptr = prev; + } + } +} + +impl Drop for DaxConnMap { + fn drop(&mut self) { + let mut ptr = self.last; + while !ptr.is_null() { + let drop_fn = unsafe { (*ptr).drop_fn }; + let prev = unsafe { (*ptr).prev }; + drop_fn(ptr); + ptr = prev; + } + } +} + +#[cfg(test)] +mod tests_of_dax { + use super::*; + use std::sync::{LazyLock, Mutex}; + + struct Logger { + log_vec: Vec, + } + + impl Logger { + fn new() -> Self { + Self { + log_vec: Vec::::new(), + } + } + fn log(&mut self, s: &str) { + self.log_vec.push(s.to_string()); + } + fn assert_logs(&self, logs: &[&str]) { + assert_eq!(self.log_vec.len(), logs.len()); + for i in 0..self.log_vec.len() { + assert_eq!(self.log_vec[i], logs[i]); + } + } + fn assert_log(&self, index: usize, log: &str) { + assert!(self.log_vec.len() > index); + assert_eq!(self.log_vec[index], log); + } + fn assert_log_either(&self, index: usize, candidates: &[&str]) { + assert!(self.log_vec.len() > index); + assert!(candidates.contains(&&self.log_vec[index].as_str())); + } + fn clear(&mut self) { + self.log_vec.clear(); + } + } + + mod tests_of_dax_src_list { + use super::*; + + mod test_of_sync_setup { + use super::*; + + static LOGGER: LazyLock> = LazyLock::new(|| Mutex::new(Logger::new())); + + struct DaxSrcA {} + + impl DaxSrcA { + fn new() -> Self { + LOGGER.lock().unwrap().log("create DaxSrcA"); + Self {} + } + } + + impl DaxSrc for DaxSrcA { + fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + LOGGER.lock().unwrap().log("setup DaxSrcA"); + Ok(()) + } + fn close(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("close DaxSrcA"); + } + fn create_dax_conn(&mut self) -> Result, Err> { + LOGGER.lock().unwrap().log("create DaxConn of DaxSrcA"); + Ok(Box::new(NoopDaxConn {})) + } + } + + impl Drop for DaxSrcA { + fn drop(&mut self) { + LOGGER.lock().unwrap().log("drop DaxSrcA"); + } + } + + struct DaxSrcB {} + + impl DaxSrcB { + fn new() -> Self { + LOGGER.lock().unwrap().log("create DaxSrcB"); + Self {} + } + } + + impl DaxSrc for DaxSrcB { + fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + LOGGER.lock().unwrap().log("setup DaxSrcB"); + Ok(()) + } + fn close(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("close DaxSrcB"); + } + fn create_dax_conn(&mut self) -> Result, Err> { + LOGGER.lock().unwrap().log("create DaxConn of DaxSrcB"); + Ok(Box::new(NoopDaxConn {})) + } + } + + impl Drop for DaxSrcB { + fn drop(&mut self) { + LOGGER.lock().unwrap().log("drop DaxSrcB"); + } + } + + fn test_sub() { + let mut ds_list = DaxSrcList::new(false); + + let ds_a = DaxSrcA::new(); + ds_list.add("a".to_string(), ds_a); + + let ds_b = DaxSrcB::new(); + ds_list.add("b".to_string(), ds_b); + + let err_map = ds_list.setup(); + assert!(err_map.is_empty()); + + ds_list.close(); + } + + #[test] + fn test_main() { + test_sub(); + + LOGGER.lock().unwrap().assert_logs(&[ + "create DaxSrcA", + "create DaxSrcB", + "setup DaxSrcA", + "setup DaxSrcB", + "close DaxSrcB", + "close DaxSrcA", + "drop DaxSrcB", + "drop DaxSrcA", + ]); + } + } + + mod test_of_async_setup { + use super::*; + use std::thread; + use std::time; + + static LOGGER: LazyLock> = LazyLock::new(|| Mutex::new(Logger::new())); + + struct DaxSrcA {} + + impl DaxSrcA { + fn new() -> Self { + LOGGER.lock().unwrap().log("create DaxSrcA"); + Self {} + } + } + + impl DaxSrc for DaxSrcA { + fn setup(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err> { + ag.add(|| { + LOGGER.lock().unwrap().log("setup DaxSrcA: start"); + thread::sleep(time::Duration::from_millis(100)); + LOGGER.lock().unwrap().log("setup DaxSrcA: end"); + Ok(()) + }); + Ok(()) + } + fn close(&mut self, ag: &mut dyn AsyncGroup) { + ag.add(|| { + LOGGER.lock().unwrap().log("close DaxSrcA: start"); + thread::sleep(time::Duration::from_millis(100)); + LOGGER.lock().unwrap().log("close DaxSrcA: end"); + Ok(()) + }); + } + fn create_dax_conn(&mut self) -> Result, Err> { + LOGGER.lock().unwrap().log("create DaxConn of DaxSrcA"); + Ok(Box::new(NoopDaxConn {})) + } + } + + impl Drop for DaxSrcA { + fn drop(&mut self) { + LOGGER.lock().unwrap().log("drop DaxSrcA"); + } + } + + struct DaxSrcB {} + + impl DaxSrcB { + fn new() -> Self { + LOGGER.lock().unwrap().log("create DaxSrcB"); + Self {} + } + } + impl DaxSrc for DaxSrcB { + fn setup(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err> { + ag.add(|| { + LOGGER.lock().unwrap().log("setup DaxSrcB: start"); + thread::sleep(time::Duration::from_millis(20)); + LOGGER.lock().unwrap().log("setup DaxSrcB: end"); + Ok(()) + }); + Ok(()) + } + fn close(&mut self, ag: &mut dyn AsyncGroup) { + ag.add(|| { + LOGGER.lock().unwrap().log("close DaxSrcB: start"); + thread::sleep(time::Duration::from_millis(20)); + LOGGER.lock().unwrap().log("close DaxSrcB: end"); + Ok(()) + }); + } + fn create_dax_conn(&mut self) -> Result, Err> { + LOGGER.lock().unwrap().log("create DaxConn of DaxSrcB"); + Ok(Box::new(NoopDaxConn {})) + } + } + + impl Drop for DaxSrcB { + fn drop(&mut self) { + LOGGER.lock().unwrap().log("drop DaxSrcB"); + } + } + + fn test_sub() { + let mut ds_list = DaxSrcList::new(false); + + let ds_a = DaxSrcA::new(); + ds_list.add("a".to_string(), ds_a); + + let ds_b = DaxSrcB::new(); + ds_list.add("b".to_string(), ds_b); + + let err_map = ds_list.setup(); + assert!(err_map.is_empty()); + + ds_list.close(); + } + + #[test] + fn test_main() { + test_sub(); + + let logger = LOGGER.lock().unwrap(); + logger.assert_log(0, "create DaxSrcA"); + logger.assert_log(1, "create DaxSrcB"); + logger.assert_log_either(2, &["setup DaxSrcA: start", "setup DaxSrcB: start"]); + logger.assert_log_either(3, &["setup DaxSrcA: start", "setup DaxSrcB: start"]); + logger.assert_log_either(4, &["setup DaxSrcB: end", "setup DaxSrcA: end"]); + logger.assert_log_either(5, &["setup DaxSrcB: end", "setup DaxSrcA: end"]); + logger.assert_log_either(6, &["close DaxSrcB: start", "close DaxSrcA: start"]); + logger.assert_log_either(7, &["close DaxSrcB: start", "close DaxSrcA: start"]); + logger.assert_log_either(8, &["close DaxSrcB: end", "close DaxSrcA: end"]); + logger.assert_log_either(9, &["close DaxSrcB: end", "close DaxSrcA: end"]); + logger.assert_log(10, "drop DaxSrcB"); + logger.assert_log(11, "drop DaxSrcA"); + } + } + } + + mod tests_of_dax_conn_map { + use super::*; + + static LOGGER: LazyLock> = LazyLock::new(|| Mutex::new(Logger::new())); + + struct DaxConnA { + committed: bool, + } + + impl DaxConnA { + fn new() -> Self { + Self { committed: false } + } + } + + impl DaxConn for DaxConnA { + fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + self.committed = true; + LOGGER.lock().unwrap().log("DaxConnA commit"); + Ok(()) + } + fn is_committed(&self) -> bool { + self.committed + } + fn rollback(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("DaxConnA rollback"); + } + fn force_back(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("DaxConnA force back"); + } + fn close(&mut self) { + LOGGER.lock().unwrap().log("DaxConnA close"); + } + } + + impl Drop for DaxConnA { + fn drop(&mut self) { + LOGGER.lock().unwrap().log("DaxConnA drop"); + } + } + + struct DaxConnB { + committed: bool, + } + + impl DaxConnB { + fn new() -> Self { + Self { committed: false } + } + } + + impl DaxConn for DaxConnB { + fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + self.committed = true; + LOGGER.lock().unwrap().log("DaxConnB commit"); + Ok(()) + } + fn is_committed(&self) -> bool { + self.committed + } + fn rollback(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("DaxConnB rollback"); + } + fn force_back(&mut self, _ag: &mut dyn AsyncGroup) { + LOGGER.lock().unwrap().log("DaxConnB force back"); + } + fn close(&mut self) { + LOGGER.lock().unwrap().log("DaxConnB close"); + } + } + + impl Drop for DaxConnB { + fn drop(&mut self) { + LOGGER.lock().unwrap().log("DaxConnB drop"); + } + } + + fn create_dax_conn_a() -> Result, Err> { + Ok(Box::new(DaxConnA::new())) + } + + fn create_dax_conn_b() -> Result, Err> { + Ok(Box::new(DaxConnB::new())) + } + + fn test_commits() { + let mut dax_conn_map = DaxConnMap::new(); + + match create_dax_conn_a() { + Ok(dax_conn) => { + let ptr = Box::into_raw(dax_conn); + let typed_ptr = ptr as *mut DaxConnA; + let boxed = unsafe { Box::from_raw(typed_ptr) }; + dax_conn_map.insert("foo", boxed); + } + Err(_) => panic!(), + } + + match create_dax_conn_b() { + Ok(dax_conn) => { + let ptr = Box::into_raw(dax_conn); + let typed_ptr = ptr as *mut DaxConnB; + let boxed = unsafe { Box::from_raw(typed_ptr) }; + dax_conn_map.insert("bar", boxed); + } + Err(_) => panic!(), + } + + match dax_conn_map.commit() { + Ok(_) => {} + Err(_) => panic!(), + } + + dax_conn_map.close(); + } + + fn test_rollbacks() { + let mut dax_conn_map = DaxConnMap::new(); + + match create_dax_conn_a() { + Ok(dax_conn) => { + let ptr = Box::into_raw(dax_conn); + let typed_ptr = ptr as *mut DaxConnA; + let boxed = unsafe { Box::from_raw(typed_ptr) }; + dax_conn_map.insert("foo", boxed); + } + Err(_) => panic!(), + } + + match create_dax_conn_b() { + Ok(dax_conn) => { + let ptr = Box::into_raw(dax_conn); + let typed_ptr = ptr as *mut DaxConnB; + let boxed = unsafe { Box::from_raw(typed_ptr) }; + dax_conn_map.insert("bar", boxed); + } + Err(_) => panic!(), + } + + dax_conn_map.rollback(); + dax_conn_map.close(); + } + + fn test_force_backs() { + let mut dax_conn_map = DaxConnMap::new(); + + match create_dax_conn_a() { + Ok(dax_conn) => { + let ptr = Box::into_raw(dax_conn); + let typed_ptr = ptr as *mut DaxConnA; + let boxed = unsafe { Box::from_raw(typed_ptr) }; + dax_conn_map.insert("foo", boxed); + } + Err(_) => panic!(), + } + + match create_dax_conn_b() { + Ok(dax_conn) => { + let ptr = Box::into_raw(dax_conn); + let typed_ptr = ptr as *mut DaxConnB; + let boxed = unsafe { Box::from_raw(typed_ptr) }; + dax_conn_map.insert("bar", boxed); + } + Err(_) => panic!(), + } + + match dax_conn_map.commit() { + Ok(_) => {} + Err(_) => panic!(), + } + + dax_conn_map.rollback(); + dax_conn_map.close(); + } + + #[test] + fn test_main() { + test_commits(); + + LOGGER.lock().unwrap().assert_logs(&[ + "DaxConnA commit", + "DaxConnB commit", + "DaxConnB close", + "DaxConnA close", + "DaxConnB drop", + "DaxConnA drop", + ]); + + LOGGER.lock().unwrap().clear(); + + test_rollbacks(); + + LOGGER.lock().unwrap().assert_logs(&[ + "DaxConnA rollback", + "DaxConnB rollback", + "DaxConnB close", + "DaxConnA close", + "DaxConnB drop", + "DaxConnA drop", + ]); + + LOGGER.lock().unwrap().clear(); + + test_force_backs(); + + LOGGER.lock().unwrap().assert_logs(&[ + "DaxConnA commit", + "DaxConnB commit", + "DaxConnA force back", + "DaxConnB force back", + "DaxConnB close", + "DaxConnA close", + "DaxConnB drop", + "DaxConnA drop", + ]); + } + } +} diff --git a/src/dax_conn.rs b/src/dax_conn.rs deleted file mode 100644 index e10f4bc..0000000 --- a/src/dax_conn.rs +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright (C) 2024 Takayuki Sato. All Rights Reserved. -// This program is free software under MIT License. -// See the file LICENSE in this distribution for more details. - -use std::any; -use std::collections::HashMap; -use std::ptr; - -use crate::async_group::{AsyncGroup, AsyncGroupAsync}; -use crate::errors; -use crate::errs::Err; - -/// Represents a connection to a data store. -/// -/// This trait declares methods: `commit`, `rollback`, `close`, etc. to work in a transaction -/// process. -pub trait DaxConn { - /// Commits the updates in a transaction. - fn commit(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err>; - - /// Checks whether updates are already committed. - fn is_committed(&self) -> bool; - - /// Rollbacks updates in a transaction. - fn rollback(&mut self, ag: &mut dyn AsyncGroup); - - /// Reverts updates forcely even if updates are already committed or this connection does not - /// have rollback mechanism. - fn force_back(&mut self, ag: &mut dyn AsyncGroup); - - /// Closes this connection. - fn close(&mut self); -} - -pub(crate) struct NoopDaxConn {} - -impl DaxConn for NoopDaxConn { - fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { - Ok(()) - } - fn is_committed(&self) -> bool { - false - } - fn rollback(&mut self, _ag: &mut dyn AsyncGroup) {} - fn force_back(&mut self, _ag: &mut dyn AsyncGroup) {} - fn close(&mut self) {} -} - -#[repr(C)] -struct DaxConnContainer<'a, C = NoopDaxConn> -where - C: DaxConn + 'static, -{ - drop_fn: fn(*const DaxConnContainer), - is_fn: fn(any::TypeId) -> bool, - commit_fn: fn(*const DaxConnContainer, &mut dyn AsyncGroup) -> Result<(), Err>, - is_committed_fn: fn(*const DaxConnContainer) -> bool, - rollback_fn: fn(*const DaxConnContainer, &mut dyn AsyncGroup), - force_back_fn: fn(*const DaxConnContainer, &mut dyn AsyncGroup), - close_fn: fn(*const DaxConnContainer), - prev: *mut DaxConnContainer<'a>, - next: *mut DaxConnContainer<'a>, - name: &'a str, - dax_conn: Box, -} - -impl<'a, C> DaxConnContainer<'a, C> -where - C: DaxConn + 'static, -{ - fn new(name: &'a str, dax_conn: Box) -> Self { - Self { - drop_fn: drop_dax_conn::, - is_fn: is_dax_conn::, - commit_fn: commit_dax_conn::, - is_committed_fn: is_committed_dax_conn::, - rollback_fn: rollback_dax_conn::, - force_back_fn: force_back_dax_conn::, - close_fn: close_dax_conn::, - prev: ptr::null_mut(), - next: ptr::null_mut(), - name, - dax_conn, - } - } -} - -fn drop_dax_conn(ptr: *const DaxConnContainer) -where - C: DaxConn + 'static, -{ - let typed_ptr = ptr as *mut DaxConnContainer; - unsafe { - drop(Box::from_raw(typed_ptr)); - } -} - -fn is_dax_conn(type_id: any::TypeId) -> bool -where - C: DaxConn + 'static, -{ - any::TypeId::of::() == type_id -} - -fn commit_dax_conn(ptr: *const DaxConnContainer, ag: &mut dyn AsyncGroup) -> Result<(), Err> -where - C: DaxConn + 'static, -{ - let typed_ptr = ptr as *mut DaxConnContainer; - unsafe { (*typed_ptr).dax_conn.commit(ag) } -} - -fn is_committed_dax_conn(ptr: *const DaxConnContainer) -> bool -where - C: DaxConn + 'static, -{ - let typed_ptr = ptr as *mut DaxConnContainer; - unsafe { (*typed_ptr).dax_conn.is_committed() } -} - -fn rollback_dax_conn(ptr: *const DaxConnContainer, ag: &mut dyn AsyncGroup) -where - C: DaxConn + 'static, -{ - let typed_ptr = ptr as *mut DaxConnContainer; - unsafe { (*typed_ptr).dax_conn.rollback(ag) } -} - -fn force_back_dax_conn(ptr: *const DaxConnContainer, ag: &mut dyn AsyncGroup) -where - C: DaxConn + 'static, -{ - let typed_ptr = ptr as *mut DaxConnContainer; - unsafe { (*typed_ptr).dax_conn.force_back(ag) }; -} - -fn close_dax_conn(ptr: *const DaxConnContainer) -where - C: DaxConn + 'static, -{ - let typed_ptr = ptr as *mut DaxConnContainer; - unsafe { (*typed_ptr).dax_conn.close() }; -} - -pub(crate) struct DaxConnMap<'a> { - head: *mut DaxConnContainer<'a>, - last: *mut DaxConnContainer<'a>, - map: HashMap<&'a str, *mut DaxConnContainer<'a>>, -} - -impl<'a> DaxConnMap<'a> { - pub(crate) fn new() -> Self { - Self { - head: ptr::null_mut(), - last: ptr::null_mut(), - map: HashMap::new(), - } - } - - pub(crate) fn insert(&mut self, name: &'a str, conn: Box) - where - C: DaxConn + 'static, - { - let boxed = Box::new(DaxConnContainer::::new(name, conn)); - let typed_ptr = Box::into_raw(boxed); - let ptr = typed_ptr.cast::(); - if self.last.is_null() { - self.head = ptr; - self.last = ptr; - } else { - unsafe { - (*self.last).next = ptr; - (*typed_ptr).prev = self.last; - } - self.last = ptr; - } - - self.map.insert(name, ptr); - } - - pub(crate) fn commit(&self) -> Result<(), Err> { - if self.last.is_null() { - return Ok(()); - } - - let mut err_map = HashMap::::new(); - let mut ag = AsyncGroupAsync::new(); - - let mut ptr = self.head; - while !ptr.is_null() { - let commit_fn = unsafe { (*ptr).commit_fn }; - let name = unsafe { (*ptr).name }; - let next = unsafe { (*ptr).next }; - if let Err(err) = commit_fn(ptr, &mut ag) { - err_map.insert(name.to_string(), err); - } - ptr = next; - } - - ag.wait(&mut err_map); - - if err_map.is_empty() { - return Ok(()); - } - - Err(Err::new(errors::DaxConn::FailToCommit { errors: err_map })) - } - - pub(crate) fn rollback(&self) { - if self.last.is_null() { - return; - } - - let mut ag = AsyncGroupAsync::new(); - - let mut ptr = self.head; - while !ptr.is_null() { - let is_committed_fn = unsafe { (*ptr).is_committed_fn }; - let rollback_fn = unsafe { (*ptr).rollback_fn }; - let force_back_fn = unsafe { (*ptr).force_back_fn }; - let next = unsafe { (*ptr).next }; - if is_committed_fn(ptr) { - force_back_fn(ptr, &mut ag); - } else { - rollback_fn(ptr, &mut ag); - } - ptr = next; - } - - let mut err_map = HashMap::::new(); - ag.wait(&mut err_map); - } - - pub(crate) fn close(&self) { - if self.last.is_null() { - return; - } - - let mut ptr = self.last; - while !ptr.is_null() { - let close_fn = unsafe { (*ptr).close_fn }; - let prev = unsafe { (*ptr).prev }; - close_fn(ptr); - ptr = prev; - } - } -} - -impl Drop for DaxConnMap<'_> { - fn drop(&mut self) { - let mut ptr = self.last; - while !ptr.is_null() { - let drop_fn = unsafe { (*ptr).drop_fn }; - let prev = unsafe { (*ptr).prev }; - drop_fn(ptr); - ptr = prev; - } - } -} - -#[cfg(test)] -mod tests_of_dax_conn { - use super::*; - use std::sync::{LazyLock, Mutex}; - - struct Logger { - log_vec: Vec, - } - - impl Logger { - fn new() -> Self { - Self { - log_vec: Vec::::new(), - } - } - fn log(&mut self, s: &str) { - self.log_vec.push(s.to_string()); - } - fn assert_logs(&self, logs: &[&str]) { - assert_eq!(self.log_vec.len(), logs.len()); - for i in 0..self.log_vec.len() { - assert_eq!(self.log_vec[i], logs[i]); - } - } - fn clear(&mut self) { - self.log_vec.clear(); - } - } - - mod test_of_dax_conn_map { - use super::*; - - static LOGGER: LazyLock> = LazyLock::new(|| Mutex::new(Logger::new())); - - struct DaxConnA { - committed: bool, - } - - impl DaxConnA { - fn new() -> Self { - Self { committed: false } - } - } - - impl DaxConn for DaxConnA { - fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { - self.committed = true; - LOGGER.lock().unwrap().log("DaxConnA commit"); - Ok(()) - } - fn is_committed(&self) -> bool { - self.committed - } - fn rollback(&mut self, _ag: &mut dyn AsyncGroup) { - LOGGER.lock().unwrap().log("DaxConnA rollback"); - } - fn force_back(&mut self, _ag: &mut dyn AsyncGroup) { - LOGGER.lock().unwrap().log("DaxConnA force back"); - } - fn close(&mut self) { - LOGGER.lock().unwrap().log("DaxConnA close"); - } - } - - impl Drop for DaxConnA { - fn drop(&mut self) { - LOGGER.lock().unwrap().log("DaxConnA drop"); - } - } - - struct DaxConnB { - committed: bool, - } - - impl DaxConnB { - fn new() -> Self { - Self { committed: false } - } - } - - impl DaxConn for DaxConnB { - fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { - self.committed = true; - LOGGER.lock().unwrap().log("DaxConnB commit"); - Ok(()) - } - fn is_committed(&self) -> bool { - self.committed - } - fn rollback(&mut self, _ag: &mut dyn AsyncGroup) { - LOGGER.lock().unwrap().log("DaxConnB rollback"); - } - fn force_back(&mut self, _ag: &mut dyn AsyncGroup) { - LOGGER.lock().unwrap().log("DaxConnB force back"); - } - fn close(&mut self) { - LOGGER.lock().unwrap().log("DaxConnB close"); - } - } - - impl Drop for DaxConnB { - fn drop(&mut self) { - LOGGER.lock().unwrap().log("DaxConnB drop"); - } - } - - fn create_dax_conn_a() -> Result, Err> { - Ok(Box::new(DaxConnA::new())) - } - - fn create_dax_conn_b() -> Result, Err> { - Ok(Box::new(DaxConnB::new())) - } - - fn commits() { - let mut dax_conn_map = DaxConnMap::new(); - - match create_dax_conn_a() { - Ok(dax_conn) => { - let ptr = Box::into_raw(dax_conn); - let typed_ptr = ptr as *mut DaxConnA; - let boxed = unsafe { Box::from_raw(typed_ptr) }; - dax_conn_map.insert("foo", boxed); - } - Err(_) => panic!(), - } - - match create_dax_conn_b() { - Ok(dax_conn) => { - let ptr = Box::into_raw(dax_conn); - let typed_ptr = ptr as *mut DaxConnB; - let boxed = unsafe { Box::from_raw(typed_ptr) }; - dax_conn_map.insert("bar", boxed); - } - Err(_) => panic!(), - } - - match dax_conn_map.commit() { - Ok(_) => {} - Err(_) => panic!(), - } - - dax_conn_map.close(); - } - - fn rollbacks() { - let mut dax_conn_map = DaxConnMap::new(); - - match create_dax_conn_a() { - Ok(dax_conn) => { - let ptr = Box::into_raw(dax_conn); - let typed_ptr = ptr as *mut DaxConnA; - let boxed = unsafe { Box::from_raw(typed_ptr) }; - dax_conn_map.insert("foo", boxed); - } - Err(_) => panic!(), - } - - match create_dax_conn_b() { - Ok(dax_conn) => { - let ptr = Box::into_raw(dax_conn); - let typed_ptr = ptr as *mut DaxConnB; - let boxed = unsafe { Box::from_raw(typed_ptr) }; - dax_conn_map.insert("bar", boxed); - } - Err(_) => panic!(), - } - - dax_conn_map.rollback(); - dax_conn_map.close(); - } - - fn force_backs() { - let mut dax_conn_map = DaxConnMap::new(); - - match create_dax_conn_a() { - Ok(dax_conn) => { - let ptr = Box::into_raw(dax_conn); - let typed_ptr = ptr as *mut DaxConnA; - let boxed = unsafe { Box::from_raw(typed_ptr) }; - dax_conn_map.insert("foo", boxed); - } - Err(_) => panic!(), - } - - match create_dax_conn_b() { - Ok(dax_conn) => { - let ptr = Box::into_raw(dax_conn); - let typed_ptr = ptr as *mut DaxConnB; - let boxed = unsafe { Box::from_raw(typed_ptr) }; - dax_conn_map.insert("bar", boxed); - } - Err(_) => panic!(), - } - - match dax_conn_map.commit() { - Ok(_) => {} - Err(_) => panic!(), - } - - dax_conn_map.rollback(); - dax_conn_map.close(); - } - - #[test] - fn test() { - commits(); - - LOGGER.lock().unwrap().assert_logs(&[ - "DaxConnA commit", - "DaxConnB commit", - "DaxConnB close", - "DaxConnA close", - "DaxConnB drop", - "DaxConnA drop", - ]); - - LOGGER.lock().unwrap().clear(); - - rollbacks(); - - LOGGER.lock().unwrap().assert_logs(&[ - "DaxConnA rollback", - "DaxConnB rollback", - "DaxConnB close", - "DaxConnA close", - "DaxConnB drop", - "DaxConnA drop", - ]); - - LOGGER.lock().unwrap().clear(); - - force_backs(); - - LOGGER.lock().unwrap().assert_logs(&[ - "DaxConnA commit", - "DaxConnB commit", - "DaxConnA force back", - "DaxConnB force back", - "DaxConnB close", - "DaxConnA close", - "DaxConnB drop", - "DaxConnA drop", - ]); - } - } -} diff --git a/src/dax_src.rs b/src/dax_src.rs deleted file mode 100644 index 0feea7b..0000000 --- a/src/dax_src.rs +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright (C) 2024 Takayuki Sato. All Rights Reserved. -// This program is free software under MIT License. -// See the file LICENSE in this distribution for more details. - -use std::collections::HashMap; -use std::ptr; - -use crate::async_group::{AsyncGroup, AsyncGroupAsync}; -use crate::dax_conn::{DaxConn, NoopDaxConn}; -use crate::errors; -use crate::errs::Err; - -/// Represents a data source which creates connections to a data store like database, etc. -/// -/// This trait declares methods: `setup`, `close`, and `create_dax_conn`. -pub trait DaxSrc { - /// Connects to a data store and prepares to create `DaxConn` instances. - /// - /// If the setup procedure is asynchronous, the `setup` method is implemented so as to use - /// `AsyncGroup`. - fn setup(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err>; - - /// Disconnects to a data store. - /// - /// If the close procedure is asynchronous, the `close` method is implemented so as to use - /// `AsyncGroup`. - fn close(&mut self, ag: &mut dyn AsyncGroup); - - /// Creates a `DaxConn` instance. - fn create_dax_conn(&self) -> Result, Err>; -} - -struct NoopDaxSrc {} - -impl DaxSrc for NoopDaxSrc { - fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { - Ok(()) - } - fn close(&mut self, _ag: &mut dyn AsyncGroup) {} - fn create_dax_conn(&self) -> Result, Err> { - Ok(Box::new(NoopDaxConn {})) - } -} - -#[repr(C)] -struct DaxSrcContainer -where - S: DaxSrc + 'static, -{ - drop_fn: fn(*const DaxSrcContainer), - setup_fn: fn(*const DaxSrcContainer, ag: &mut dyn AsyncGroup) -> Result<(), Err>, - close_fn: fn(*const DaxSrcContainer, ag: &mut dyn AsyncGroup), - prev: *mut DaxSrcContainer, - next: *mut DaxSrcContainer, - name: String, - dax_src: S, -} - -impl DaxSrcContainer -where - S: DaxSrc + 'static, -{ - fn new(name: String, dax_src: S) -> Self { - Self { - drop_fn: drop_dax_src_container::, - setup_fn: setup_dax_src_container::, - close_fn: close_dax_src_container::, - prev: ptr::null_mut(), - next: ptr::null_mut(), - name, - dax_src, - } - } - - fn name(&self) -> &str { - &self.name - } -} - -fn drop_dax_src_container(ptr: *const DaxSrcContainer) -where - S: DaxSrc + 'static, -{ - let typed_ptr = ptr as *mut DaxSrcContainer; - drop(unsafe { Box::from_raw(typed_ptr) }); -} - -fn setup_dax_src_container( - ptr: *const DaxSrcContainer, - ag: &mut dyn AsyncGroup, -) -> Result<(), Err> -where - S: DaxSrc + 'static, -{ - let typed_ptr = ptr as *mut DaxSrcContainer; - unsafe { (*typed_ptr).dax_src.setup(ag) } -} - -fn close_dax_src_container(ptr: *const DaxSrcContainer, ag: &mut dyn AsyncGroup) -where - S: DaxSrc + 'static, -{ - let typed_ptr = ptr as *mut DaxSrcContainer; - unsafe { (*typed_ptr).dax_src.close(ag) }; -} - -struct DaxSrcList { - head: *mut DaxSrcContainer, - last: *mut DaxSrcContainer, - fixed: bool, -} - -impl DaxSrcList { - const fn new() -> Self { - Self { - head: ptr::null_mut(), - last: ptr::null_mut(), - fixed: false, - } - } - - fn add(&mut self, name: String, ds: S) - where - S: DaxSrc + 'static, - { - if self.fixed { - return; - } - - let boxed = Box::new(DaxSrcContainer::::new(name, ds)); - let typed_ptr = Box::into_raw(boxed); - let ptr = typed_ptr.cast::(); - if self.last.is_null() { - self.head = ptr; - self.last = ptr; - } else { - unsafe { - (*self.last).next = ptr; - (*typed_ptr).prev = self.last; - } - self.last = ptr; - } - } - - fn fix(&mut self) { - self.fixed = true; - } - - fn setup(&mut self) -> HashMap { - let mut err_map = HashMap::new(); - - if self.head.is_null() { - return err_map; - } - - let mut ag = AsyncGroupAsync::new(); - - let mut ptr = self.head; - while !ptr.is_null() { - let setup_fn = unsafe { (*ptr).setup_fn }; - let next = unsafe { (*ptr).next }; - ag.name = unsafe { &(*ptr).name }; - if let Err(err) = setup_fn(ptr, &mut ag) { - err_map.insert(ag.name.to_string(), err); - } - ptr = next; - } - - ag.wait(&mut err_map); - - err_map - } - - fn close(&mut self) { - let mut err_map = HashMap::new(); - - if self.head.is_null() { - return; - } - - let mut ag = AsyncGroupAsync::new(); - - let mut ptr = self.last; - while !ptr.is_null() { - let close_fn = unsafe { (*ptr).close_fn }; - let prev = unsafe { (*ptr).prev }; - close_fn(ptr, &mut ag); - ptr = prev; - } - - ag.wait(&mut err_map); - } -} - -impl Drop for DaxSrcList { - fn drop(&mut self) { - let mut ptr = self.last; - while !ptr.is_null() { - let drop_fn = unsafe { (*ptr).drop_fn }; - let prev = unsafe { (*ptr).prev }; - drop_fn(ptr); - ptr = prev; - } - } -} - -static mut GLOBAL_DAX_SRC_LIST: DaxSrcList = DaxSrcList::new(); - -/// Registers a global `DaxSrc` with its name. -/// -/// By registering with this funciton, `DaxSrc` becomes able to create a `DaxConn` that can be -/// used within a transaction. -pub fn uses(name: &str, ds: S) { - unsafe { - GLOBAL_DAX_SRC_LIST.add(name.to_string(), ds); - } -} - -/// Makes all globally registered DaxSrc(s) usable. -/// -/// This function forbids adding more global `DaxSrc`(s), and calls each `setup` method of all -/// registered `DaxSrc`(s) -pub fn setup() -> Result<(), Err> { - unsafe { - GLOBAL_DAX_SRC_LIST.fix(); - - let err_map = GLOBAL_DAX_SRC_LIST.setup(); - if err_map.len() > 0 { - return Err(Err::new(errors::DaxSrc::FailToSetupGlobal { - errors: err_map, - })); - } - } - - Ok(()) -} - -/// Closes and frees each resource of registered global `DaxSrc`(s). -/// -// This function should always be called before an application ends. -pub fn close() { - unsafe { - GLOBAL_DAX_SRC_LIST.close(); - } -} - -/// Calls `setup` function, the argument function, and `close` function in order. -/// -/// If `setup` function or the argument fucntion fails, this fucntion stops calling other functions -/// and return an `Err` containing the error reason. -pub fn start_app(app: fn() -> Result<(), Err>) -> Result<(), Err> { - if let Err(err) = setup() { - return Err(err); - } - - if let Err(err) = app() { - return Err(err); - } - - close(); - - Ok(()) -} - -#[cfg(test)] -mod tests_of_dax_src_list { - use super::*; - use std::sync::LazyLock; - use std::sync::Mutex; - - struct Logger { - log_vec: Vec, - } - - impl Logger { - fn new() -> Self { - Self { - log_vec: Vec::::new(), - } - } - fn log(&mut self, s: &str) { - self.log_vec.push(s.to_string()); - } - fn assert_logs(&self, logs: &[&str]) { - assert_eq!(self.log_vec.len(), logs.len()); - for i in 0..self.log_vec.len() { - assert_eq!(self.log_vec[i], logs[i]); - } - } - } - - mod test_of_sync_setup { - use super::*; - - static LOGGER: LazyLock> = LazyLock::new(|| Mutex::new(Logger::new())); - - struct DaxSrcA {} - - impl DaxSrcA { - fn new() -> Self { - LOGGER.lock().unwrap().log("create DaxSrcA"); - Self {} - } - } - - impl DaxSrc for DaxSrcA { - fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { - LOGGER.lock().unwrap().log("setup DaxSrcA"); - Ok(()) - } - fn close(&mut self, _ag: &mut dyn AsyncGroup) { - LOGGER.lock().unwrap().log("close DaxSrcA"); - } - fn create_dax_conn(&self) -> Result, Err> { - LOGGER.lock().unwrap().log("create DaxConn of DaxSrcA"); - Ok(Box::new(NoopDaxConn {})) - } - } - - impl Drop for DaxSrcA { - fn drop(&mut self) { - LOGGER.lock().unwrap().log("drop DaxSrcA"); - } - } - - struct DaxSrcB {} - - impl DaxSrcB { - fn new() -> Self { - LOGGER.lock().unwrap().log("create DaxSrcB"); - Self {} - } - } - impl DaxSrc for DaxSrcB { - fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { - LOGGER.lock().unwrap().log("setup DaxSrcB"); - Ok(()) - } - fn close(&mut self, _ag: &mut dyn AsyncGroup) { - LOGGER.lock().unwrap().log("close DaxSrcB"); - } - fn create_dax_conn(&self) -> Result, Err> { - LOGGER.lock().unwrap().log("create DaxConn of DaxSrcB"); - Ok(Box::new(NoopDaxConn {})) - } - } - - impl Drop for DaxSrcB { - fn drop(&mut self) { - LOGGER.lock().unwrap().log("drop DaxSrcB"); - } - } - - fn dax_src_list() { - let mut ds_list = DaxSrcList::new(); - - let ds_a = DaxSrcA::new(); - ds_list.add("a".to_string(), ds_a); - - let ds_b = DaxSrcB::new(); - ds_list.add("b".to_string(), ds_b); - - let err_map = ds_list.setup(); - assert!(err_map.is_empty()); - - ds_list.close(); - } - - #[test] - fn test() { - dax_src_list(); - - LOGGER.lock().unwrap().assert_logs(&[ - "create DaxSrcA", - "create DaxSrcB", - "setup DaxSrcA", - "setup DaxSrcB", - "close DaxSrcB", - "close DaxSrcA", - "drop DaxSrcB", - "drop DaxSrcA", - ]); - } - } - - mod test_of_async_setup { - use super::*; - use std::thread; - use std::time; - - static LOGGER: LazyLock> = LazyLock::new(|| Mutex::new(Logger::new())); - - struct DaxSrcA {} - - impl DaxSrcA { - fn new() -> Self { - LOGGER.lock().unwrap().log("create DaxSrcA"); - Self {} - } - } - - impl DaxSrc for DaxSrcA { - fn setup(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err> { - ag.add(|| { - LOGGER.lock().unwrap().log("setup DaxSrcA: start"); - thread::sleep(time::Duration::from_millis(100)); - LOGGER.lock().unwrap().log("setup DaxSrcA: end"); - Ok(()) - }); - Ok(()) - } - fn close(&mut self, ag: &mut dyn AsyncGroup) { - ag.add(|| { - thread::sleep(time::Duration::from_millis(10)); - LOGGER.lock().unwrap().log("close DaxSrcA: start"); - thread::sleep(time::Duration::from_millis(100)); - LOGGER.lock().unwrap().log("close DaxSrcA: end"); - Ok(()) - }); - } - fn create_dax_conn(&self) -> Result, Err> { - LOGGER.lock().unwrap().log("create DaxConn of DaxSrcA"); - Ok(Box::new(NoopDaxConn {})) - } - } - - impl Drop for DaxSrcA { - fn drop(&mut self) { - LOGGER.lock().unwrap().log("drop DaxSrcA"); - } - } - - struct DaxSrcB {} - - impl DaxSrcB { - fn new() -> Self { - LOGGER.lock().unwrap().log("create DaxSrcB"); - Self {} - } - } - impl DaxSrc for DaxSrcB { - fn setup(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err> { - ag.add(|| { - thread::sleep(time::Duration::from_millis(10)); - LOGGER.lock().unwrap().log("setup DaxSrcB: start"); - thread::sleep(time::Duration::from_millis(20)); - LOGGER.lock().unwrap().log("setup DaxSrcB: end"); - Ok(()) - }); - Ok(()) - } - fn close(&mut self, ag: &mut dyn AsyncGroup) { - ag.add(|| { - LOGGER.lock().unwrap().log("close DaxSrcB: start"); - thread::sleep(time::Duration::from_millis(20)); - LOGGER.lock().unwrap().log("close DaxSrcB: end"); - Ok(()) - }); - } - fn create_dax_conn(&self) -> Result, Err> { - LOGGER.lock().unwrap().log("create DaxConn of DaxSrcB"); - Ok(Box::new(NoopDaxConn {})) - } - } - - impl Drop for DaxSrcB { - fn drop(&mut self) { - LOGGER.lock().unwrap().log("drop DaxSrcB"); - } - } - - fn dax_src_list() { - let mut ds_list = DaxSrcList::new(); - - let ds_a = DaxSrcA::new(); - ds_list.add("a".to_string(), ds_a); - - let ds_b = DaxSrcB::new(); - ds_list.add("b".to_string(), ds_b); - - let err_map = ds_list.setup(); - assert!(err_map.is_empty()); - - ds_list.close(); - } - - #[test] - fn test() { - dax_src_list(); - - LOGGER.lock().unwrap().assert_logs(&[ - "create DaxSrcA", - "create DaxSrcB", - "setup DaxSrcA: start", - "setup DaxSrcB: start", - "setup DaxSrcB: end", - "setup DaxSrcA: end", - "close DaxSrcB: start", - "close DaxSrcA: start", - "close DaxSrcB: end", - "close DaxSrcA: end", - "drop DaxSrcB", - "drop DaxSrcA", - ]); - } - } -} diff --git a/src/errors.rs b/src/errors.rs index d4b7389..3f1aae3 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -44,3 +44,9 @@ pub enum DaxConn { errors: HashMap, }, } + +#[derive(Debug)] +pub enum DaxBase { + FailToCastDaxConn { name: String, to_type: &'static str }, + FailToGetDaxConn { name: String, to_type: &'static str }, +} diff --git a/src/lib.rs b/src/lib.rs index f031b84..a546ce2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,18 +3,87 @@ // See the file LICENSE in this distribution for more details. mod async_group; -mod dax_conn; -mod dax_src; +mod dax; mod errs; /// Enums for errors that can occur in this `sabi` crate. pub mod errors; -pub use errs::Err; pub use async_group::AsyncGroup; -pub use dax_conn::DaxConn; -pub use dax_src::DaxSrc; -pub use dax_src::close; -pub use dax_src::setup; -pub use dax_src::start_app; -pub use dax_src::uses; +pub use dax::DaxBaseImpl; +pub use dax::{close, setup, start_app, uses}; +pub use errs::Err; + +/// The trait for a set of data access methods. +/// +/// This trait is inherited by `Dax` implementations for data stores, and each `Dax` implementation +/// defines data access methods to each data store. +/// In data access methods, `DaxConn` instances connected to data stores can be obtained with +/// `get_dax_conn` method. +pub trait Dax { + /// Gets a `DaxConn` instances by the registered name and casts it to the specified type. + fn get_dax_conn(&mut self, name: &str) -> Result<&C, Err>; +} + +/// Represents a data source which creates connections to a data store like database, etc. +pub trait DaxSrc { + /// Connects to a data store and prepares to create `DaxConn` instances. + /// + /// If the setup procedure is asynchronous, use the `AsyncGroup` argument. + fn setup(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err>; + + /// Disconnects to a data store. + /// + /// If the closing procedure is asynchronous, use the `AsyncGroup` argument. + fn close(&mut self, ag: &mut dyn AsyncGroup); + + /// Creates a `DaxConn` instance. + fn create_dax_conn(&mut self) -> Result, Err>; +} + +struct NoopDaxSrc {} + +impl DaxSrc for NoopDaxSrc { + fn setup(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + Ok(()) + } + fn close(&mut self, _ag: &mut dyn AsyncGroup) {} + fn create_dax_conn(&mut self) -> Result, Err> { + Ok(Box::new(NoopDaxConn {})) + } +} + +/// Represents a connection to a data store. +/// +/// This trait provides method interfaces to work in a transaction process. +pub trait DaxConn { + /// Commits the updates in a transaction. + fn commit(&mut self, ag: &mut dyn AsyncGroup) -> Result<(), Err>; + + /// Checks whether updates are already committed. + fn is_committed(&self) -> bool; + + /// Rollbacks updates in a transaction. + fn rollback(&mut self, ag: &mut dyn AsyncGroup); + + /// Reverts updates forcely even if updates are already committed or this connection does not + /// have rollback mechanism. + fn force_back(&mut self, ag: &mut dyn AsyncGroup); + + /// Closes this connection. + fn close(&mut self); +} + +struct NoopDaxConn {} + +impl DaxConn for NoopDaxConn { + fn commit(&mut self, _ag: &mut dyn AsyncGroup) -> Result<(), Err> { + Ok(()) + } + fn is_committed(&self) -> bool { + false + } + fn rollback(&mut self, _ag: &mut dyn AsyncGroup) {} + fn force_back(&mut self, _ag: &mut dyn AsyncGroup) {} + fn close(&mut self) {} +}