use anyhow::Result;
use futures::future::{BoxFuture, Either, Ready, ready};
use serde::{Deserialize, Serialize};
use tokio::sync::{Mutex, watch};
use std::sync::Arc;
use crate::G2;
use kvbm_logical::blocks::ImmutableBlock;
use super::onboarding::{OnboardingStatus, SessionHandle};
use super::session::SessionId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum StagingMode {
Hold,
Prepare,
#[default]
Full,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FindMatchesOptions {
pub search_remote: bool,
pub staging_mode: StagingMode,
}
#[derive(Debug)]
pub enum FindMatchesResult {
Ready(ReadyResult),
AsyncSession(AsyncSessionResult),
}
#[derive(Debug)]
pub struct ReadyResult {
blocks: Vec<ImmutableBlock<G2>>,
}
impl ReadyResult {
pub fn new(blocks: Vec<ImmutableBlock<G2>>) -> Self {
Self { blocks }
}
pub fn g2_count(&self) -> usize {
self.blocks.len()
}
pub fn take_g2_blocks(&mut self) -> Vec<ImmutableBlock<G2>> {
std::mem::take(&mut self.blocks)
}
pub fn blocks(&self) -> &[ImmutableBlock<G2>] {
&self.blocks
}
}
#[derive(Debug)]
pub struct AsyncSessionResult {
session_id: SessionId,
status_rx: watch::Receiver<OnboardingStatus>,
blocks: Arc<Mutex<Option<Vec<ImmutableBlock<G2>>>>>,
session_handle: Option<SessionHandle>,
}
impl AsyncSessionResult {
pub fn new(
session_id: SessionId,
status_rx: watch::Receiver<OnboardingStatus>,
blocks: Arc<Mutex<Option<Vec<ImmutableBlock<G2>>>>>,
session_handle: Option<SessionHandle>,
) -> Self {
Self {
session_id,
status_rx,
blocks,
session_handle,
}
}
pub fn session_id(&self) -> SessionId {
self.session_id
}
pub fn status(&self) -> OnboardingStatus {
self.status_rx.borrow().clone()
}
pub fn session_handle(&self) -> Option<&SessionHandle> {
self.session_handle.as_ref()
}
pub fn get_blocks_count(&self) -> Option<usize> {
self.blocks.try_lock().ok()?.as_ref().map(|v| v.len())
}
pub fn wait_for_completion(&self) -> BoxFuture<'static, Result<()>> {
let mut status_rx = self.status_rx.clone();
Box::pin(async move {
status_rx
.wait_for(|status| {
matches!(
status,
OnboardingStatus::Complete { .. }
| OnboardingStatus::Holding { .. }
| OnboardingStatus::Prepared { .. }
)
})
.await
.map_err(|e| anyhow::anyhow!("failed to wait for completion: {e}"))?;
Ok(())
})
}
}
impl FindMatchesResult {
pub fn is_ready(&self) -> bool {
matches!(self, FindMatchesResult::Ready(_))
}
pub fn is_async(&self) -> bool {
matches!(self, FindMatchesResult::AsyncSession(_))
}
pub fn as_ready(&self) -> Option<&ReadyResult> {
match self {
FindMatchesResult::Ready(r) => Some(r),
FindMatchesResult::AsyncSession(_) => None,
}
}
pub fn as_ready_mut(&mut self) -> Option<&mut ReadyResult> {
match self {
FindMatchesResult::Ready(r) => Some(r),
FindMatchesResult::AsyncSession(_) => None,
}
}
pub fn as_async(&self) -> Option<&AsyncSessionResult> {
match self {
FindMatchesResult::Ready(_) => None,
FindMatchesResult::AsyncSession(a) => Some(a),
}
}
pub fn as_async_mut(&mut self) -> Option<&mut AsyncSessionResult> {
match self {
FindMatchesResult::Ready(_) => None,
FindMatchesResult::AsyncSession(a) => Some(a),
}
}
pub fn g2_count(&self) -> usize {
match self {
FindMatchesResult::Ready(r) => r.g2_count(),
FindMatchesResult::AsyncSession(a) => a.get_blocks_count().unwrap_or(0),
}
}
pub fn take_g2_blocks(&mut self) -> Option<Vec<ImmutableBlock<G2>>> {
match self {
FindMatchesResult::Ready(r) => Some(r.take_g2_blocks()),
FindMatchesResult::AsyncSession(a) => a.blocks.try_lock().ok()?.take(),
}
}
pub fn session_id(&self) -> Option<SessionId> {
match self {
FindMatchesResult::Ready(_) => None,
FindMatchesResult::AsyncSession(a) => Some(a.session_id()),
}
}
pub fn wait_for_completion(&self) -> Either<Ready<Result<()>>, BoxFuture<'static, Result<()>>> {
match self {
FindMatchesResult::Ready(_) => Either::Left(ready(Ok(()))),
FindMatchesResult::AsyncSession(async_session) => {
Either::Right(async_session.wait_for_completion())
}
}
}
}