// Copyright © 2019-2020 The Radicle Foundation // // This file is part of radicle-link, distributed under the GPLv3 with Radicle // Linking Exception. For full terms see the included LICENSE file. use std::{ marker::PhantomData, ops::{Deref, DerefMut}, path::PathBuf, sync::Arc, }; use deadpool::managed::{self, Manager, Object, RecycleResult}; use parking_lot::RwLock; use std_ext::Void; use thiserror::Error; use crate::{read, signature::UserInfo, write, Read, Write}; #[derive(Debug, Error)] #[non_exhaustive] pub enum InitError { #[error(transparent)] Read(#[from] read::error::Init), #[error(transparent)] Write(#[from] write::error::Init), } pub type Pool = deadpool::managed::Pool; pub type PoolError = managed::PoolError; #[async_trait] pub trait Pooled { async fn get(&self) -> Result, PoolError>; } #[async_trait] impl Pooled for Pool { async fn get(&self) -> Result, PoolError> { self.get().await.map(PooledRef::from) } } /// A reference to a pooled storage. /// /// The `S` parameter can be filled by [`Write`] for read-write access or /// [`Read`] for read-only access. pub struct PooledRef(Object); impl Deref for PooledRef { type Target = S; fn deref(&self) -> &Self::Target { self.0.deref() } } impl DerefMut for PooledRef { fn deref_mut(&mut self) -> &mut Self::Target { self.0.deref_mut() } } impl AsRef for PooledRef { fn as_ref(&self) -> &S { self } } impl AsMut for PooledRef { fn as_mut(&mut self) -> &mut S { self } } impl AsRef for PooledRef { fn as_ref(&self) -> &Read { self.0.read_only() } } impl From> for PooledRef { fn from(obj: Object) -> Self { Self(obj) } } #[derive(Clone)] pub struct Initialised(Arc>); impl Initialised { pub fn no() -> Self { Self(Arc::new(RwLock::new(false))) } } pub struct Writer { init: Initialised, } #[derive(Clone)] pub struct Config { root: PathBuf, info: UserInfo, write: W, } pub type ReadConfig = Config>; pub type WriteConfig = Config; impl ReadConfig { pub fn new(root: PathBuf, info: UserInfo) -> Self { Config { root, info, write: PhantomData, } } pub fn write(self, init: Initialised) -> WriteConfig { Config { root: self.root, info: self.info, write: Writer { init }, } } } #[async_trait] impl Manager for ReadConfig { async fn create(&self) -> Result { Read::open(&self.root).map_err(InitError::from) } async fn recycle(&self, _: &mut Read) -> RecycleResult { Ok(()) } } impl WriteConfig { pub fn new(root: PathBuf, info: UserInfo, init: Initialised) -> Self { Self { root, info, write: Writer { init }, } } fn mk_storage(&self) -> Result { Write::open(&self.root, self.info.clone()).map_err(InitError::from) } } #[async_trait] impl Manager for WriteConfig { async fn create(&self) -> Result { let initialised = self.write.init.0.read(); if *initialised { self.mk_storage() } else { drop(initialised); let mut initialised = self.write.init.0.write(); self.mk_storage() .map(|storage| { *initialised = true; storage }) .map_err(InitError::from) } } async fn recycle(&self, _: &mut Write) -> RecycleResult { Ok(()) } }