kanamaru 0.1.0

Build typed Tauri plugins with the power of Protobuf Buffers
Documentation
use std::{fmt::Debug, sync::Arc};

use prost::Message;
use serde::Deserialize;
use tauri::{
    http::HeaderMap,
    ipc::{Channel, InvokeBody, InvokeMessage},
    Runtime, Webview,
};

use crate::{utils::CancellationTokenListener, Status};

use super::{InvokeMessageToRequestError, RawRequest, RawRequestToRequestError, RequestBase};

#[derive(Clone)]
pub struct UnaryRequest<R, M>
where
    M: Message + Clone + Default,
    R: Runtime,
{
    token: Arc<CancellationTokenListener<Webview<R>, R>>,
    message: M,
    channel_status: Channel<Status>,
    headers: HeaderMap,
}

impl<R: Runtime, M: Message + Clone + Default> Debug for UnaryRequest<R, M> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("UnaryRequest")
            .field("token", &self.token)
            .field("message", &self.message)
            .field("channel_status", &())
            .field("headers", &self.headers)
            .finish()
    }
}

impl<R, M> UnaryRequest<R, M>
where
    M: Message + Clone + Default,
    R: Runtime,
{
    pub fn new(
        token: Arc<CancellationTokenListener<Webview<R>, R>>,
        raw_reqwest: RawRequest,
    ) -> Result<Self, RawRequestToRequestError> {
        if let Some(payload) = raw_reqwest.payload {
            Ok(Self {
                channel_status: raw_reqwest
                    .status_channel
                    .channel_on(token.listener().clone()),
                token,
                message: payload.extract_message()?,
                headers: payload.metadata,
            })
        } else {
            Err(RawRequestToRequestError::MissingPayload)
        }
    }

    pub fn into_inner(self) -> M {
        self.message
    }
    pub fn message(&self) -> &M {
        self.as_ref()
    }
    pub fn headers_ref(&self) -> &HeaderMap {
        &self.headers
    }
    pub fn headers(&self) -> HeaderMap {
        self.headers_ref().clone()
    }
    pub fn map_message<F, M1>(self, map_fn: F) -> UnaryRequest<R, M1>
    where
        F: FnOnce(M) -> M1,
        M1: Message + Clone + Default,
    {
        UnaryRequest {
            token: self.token,
            message: map_fn(self.message),
            channel_status: self.channel_status,
            headers: self.headers,
        }
    }
    pub fn change_message<F, M1>(self, message: M1) -> UnaryRequest<R, M1>
    where
        F: FnOnce(M) -> M1,
        M1: Message + Clone + Default,
    {
        UnaryRequest {
            token: self.token,
            message,
            channel_status: self.channel_status,
            headers: self.headers,
        }
    }
}

impl<R, M> AsRef<M> for UnaryRequest<R, M>
where
    M: Message + Clone + Default,
    R: Runtime,
{
    fn as_ref(&self) -> &M {
        &self.message
    }
}

impl<M, R> TryFrom<&InvokeMessage<R>> for UnaryRequest<R, M>
where
    M: Message + Clone + Default,
    R: Runtime,
{
    type Error = InvokeMessageToRequestError;
    fn try_from(value: &InvokeMessage<R>) -> Result<Self, Self::Error> {
        if let InvokeBody::Json(payload) = value.payload() {
            let raw_req: RawRequest = Deserialize::deserialize(payload)?;
            let token = raw_req.cancel_token(value.webview());
            Ok(UnaryRequest::new(Arc::new(token), raw_req)?)
        } else {
            Err(InvokeMessageToRequestError::RawRequestToRequest(
                RawRequestToRequestError::InvalidPayloadFormat,
            ))
        }
    }
}

impl<M, R> RequestBase<R> for UnaryRequest<R, M>
where
    M: Message + Clone + Default,
    R: Runtime,
{
    fn token(&self) -> Arc<CancellationTokenListener<Webview<R>, R>> {
        self.token.clone()
    }
    fn status_channel(&self) -> tauri::ipc::Channel<crate::Status> {
        self.channel_status.clone()
    }
}