ydb 0.16.0

Crate contains generated low-level grpc code from YDB API protobuf, used as base for ydb crate
Documentation
use super::{
    handler::{
        FromHandlerToService, FromServerToServiceRx, FromServiceToServerRx, Handler, Incoming,
        Reply,
    },
    topic::{default::TopicDefaultHandler, handler::TopicTx, MockTopicService},
};
use futures_util::stream;
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tokio_util::sync::CancellationToken;
use tonic::transport::Server;
use ydb_grpc::ydb_proto::topic::v1::topic_service_server::TopicServiceServer;

struct ForwardChannels {
    topic_tx: TopicTx,
}

impl ForwardChannels {
    fn resend(&self, reply: Reply) {
        match reply {
            Reply::Topic(reply) => {
                let _ = self.topic_tx.send(reply);
            }
            Reply::Scheme(_) => unimplemented!(),
        }
    }
}

struct DefaultHandler {
    topic: TopicDefaultHandler,
}

impl DefaultHandler {
    fn with_tx(tx: FromHandlerToService) -> Self {
        Self {
            topic: TopicDefaultHandler::with_tx(tx.clone()),
        }
    }
}

impl Handler for DefaultHandler {
    fn set_channel(&mut self, _tx: FromHandlerToService) {
        unimplemented!()
    }

    fn handle(&self, incoming: Incoming) -> Option<Incoming> {
        match incoming {
            Incoming::Topic(_) => self.topic.handle(incoming),
            Incoming::Scheme(_) => todo!(),
        }
    }
}

pub struct MockServer {
    endpoint: String,
    addr: SocketAddr,
    shutdown: CancellationToken,
    _tonic_services: tokio::task::JoinHandle<()>,
}

impl MockServer {
    pub async fn start(handler: impl Handler) -> (Self, FromHandlerToService) {
        let listener = TcpListener::bind("127.0.0.1:0")
            .await
            .expect("mock server failed to bind tcp listener");
        let addr = listener
            .local_addr()
            .expect("mock server failed to read local address");
        Self::start_with_listener(listener, addr, handler).await
    }

    async fn start_with_listener(
        listener: TcpListener,
        addr: SocketAddr,
        mut handler: impl Handler,
    ) -> (Self, FromHandlerToService) {
        let (from_service_to_server_tx, from_service_to_server_rx) =
            tokio::sync::mpsc::unbounded_channel();
        let (from_server_to_service_tx, from_server_to_service_rx) =
            tokio::sync::mpsc::unbounded_channel();
        let (topic_tx, topic_rx) = tokio::sync::mpsc::unbounded_channel();

        let topic_service = MockTopicService::new(from_service_to_server_tx, topic_rx);

        let tcp_streams = stream::unfold(listener, |listener| async {
            Some((listener.accept().await.map(|(stream, _)| stream), listener))
        });

        let shutdown = CancellationToken::new();
        let shutdown_signal = shutdown.clone();

        let tonic_services = tokio::spawn(async move {
            let result = Server::builder()
                .add_service(TopicServiceServer::new(topic_service))
                .serve_with_incoming_shutdown(tcp_streams, shutdown_signal.cancelled())
                .await;

            if let Err(err) = result {
                panic!("mock server failed: {err}");
            }
        });

        handler.set_channel(from_server_to_service_tx.clone());

        tokio::spawn(Self::dispatch_loop(
            from_service_to_server_rx,
            handler,
            DefaultHandler::with_tx(from_server_to_service_tx.clone()),
        ));

        tokio::spawn(Self::forwarding_loop(
            ForwardChannels { topic_tx },
            from_server_to_service_rx,
        ));

        let server = Self {
            endpoint: endpoint(addr),
            addr,
            shutdown,
            _tonic_services: tonic_services,
        };

        (server, from_server_to_service_tx)
    }

    async fn dispatch_loop(
        mut rx: FromServiceToServerRx,
        handler: impl Handler,
        default: impl Handler,
    ) {
        while let Some(incoming) = rx.recv().await {
            let Some(incoming) = handler.handle(incoming) else {
                continue;
            };

            let _ = default.handle(incoming);
        }
    }

    async fn forwarding_loop(channels: ForwardChannels, mut rx: FromServerToServiceRx) {
        while let Some(reply) = rx.recv().await {
            channels.resend(reply);
        }
    }

    pub fn endpoint(&self) -> &str {
        &self.endpoint
    }

    pub(crate) fn addr(&self) -> SocketAddr {
        self.addr
    }
}

impl Drop for MockServer {
    fn drop(&mut self) {
        self.shutdown.cancel();
    }
}

fn endpoint(addr: SocketAddr) -> String {
    format!("grpc://{}:{}", addr.ip(), addr.port())
}