tc-server 0.1.0

TinyChain's cluster server logic, including peer discovery and replication
Documentation
use std::fmt;

use log::debug;
use safecast::TryCastInto;

use tc_error::*;
use tc_state::CacheBlock;
use tc_transact::fs;
use tc_transact::public::*;
use tc_transact::Transaction;
use tcgeneric::{PathSegment, TCPath};

use crate::cluster::dir::{Dir, DirCreate, DirCreateItem, DirEntry, DirItem};
use crate::cluster::{Cluster, Schema};
use crate::{State, Txn};

struct DirHandler<'a, T> {
    dir: &'a Dir<T>,
}

impl<'a, T> Handler<'a, State> for DirHandler<'a, T>
where
    T: DirItem + fmt::Debug,
    Dir<T>: fs::Persist<CacheBlock, Txn = Txn, Schema = Schema> + Route<State> + fmt::Debug,
    DirEntry<T>: Clone,
    Cluster<T>: fs::Persist<CacheBlock, Txn = Txn, Schema = Schema>,
    Cluster<Dir<T>>: fs::Persist<CacheBlock, Txn = Txn, Schema = Schema> + Clone,
{
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|txn, key| {
            Box::pin(async move {
                if key.is_none() {
                    let entries = self.dir.entries(*txn.id()).await?;
                    let entries = entries
                        .map(|(name, entry)| ((*name).clone(), State::from(entry.is_dir())))
                        .collect();

                    Ok(State::Map(entries))
                } else {
                    let name =
                        key.try_cast_into(|v| TCError::unexpected(v, "a directory entry name"))?;

                    let entry = self.dir.entry(*txn.id(), &name).await?;
                    let entry = entry.ok_or_else(|| not_found!("dir entry {name}"))?;
                    Ok(entry.is_dir().into())
                }
            })
        }))
    }

    fn put<'b>(self: Box<Self>) -> Option<PutHandler<'a, 'b, Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|txn, key, value| {
            Box::pin(async move {
                let name: PathSegment =
                    key.try_cast_into(|v| TCError::unexpected(v, "a directory name"))?;

                if value.try_into()? {
                    self.dir.create_dir(txn, name).await?;
                } else {
                    self.dir.create_item(txn, name).await?;
                }

                Ok(())
            })
        }))
    }
}

impl<'a, T> From<&'a Dir<T>> for DirHandler<'a, T> {
    fn from(dir: &'a Dir<T>) -> Self {
        Self { dir }
    }
}

impl<T> Route<State> for Dir<T>
where
    T: DirItem + fmt::Debug,
    Dir<T>: fs::Persist<CacheBlock, Txn = Txn, Schema = Schema> + fmt::Debug,
    DirEntry<T>: Clone,
    Cluster<T>: fs::Persist<CacheBlock, Txn = Txn, Schema = Schema>,
    Cluster<Dir<T>>: fs::Persist<CacheBlock, Txn = Txn, Schema = Schema> + Clone,
{
    fn route<'a>(&'a self, path: &'a [PathSegment]) -> Option<Box<dyn Handler<'a, State> + 'a>> {
        if path.is_empty() {
            Some(Box::new(DirHandler::from(self)))
        } else {
            debug!("request to {} routed to parent dir", TCPath::from(path));
            None
        }
    }
}