1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use core::marker::PhantomData;

use anyhow::Context as _;
use bytes::{BufMut as _, Bytes, BytesMut};
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt as _};
use tokio_util::codec::Encoder;
use tracing::{instrument, trace};
use wasm_tokio::{CoreNameEncoder, CoreVecEncoderBytes};

use crate::frame::conn::{Incoming, Outgoing};
use crate::frame::{Conn, ConnHandler, PROTOCOL};

/// Defines invocation behavior
#[derive(Clone)]
pub struct InvokeBuilder<H = ()>(PhantomData<H>)
where
    H: ?Sized;

impl<H> InvokeBuilder<H> {
    /// Invoke function `func` on instance `instance`
    #[instrument(level = "trace", skip_all)]
    pub async fn invoke<P, I, O>(
        self,
        mut tx: O,
        rx: I,
        instance: &str,
        func: &str,
        params: Bytes,
        paths: impl AsRef<[P]> + Send,
    ) -> anyhow::Result<(Outgoing, Incoming)>
    where
        P: AsRef<[Option<usize>]> + Send + Sync,
        I: AsyncRead + Unpin + Send + 'static,
        O: AsyncWrite + Unpin + Send + 'static,
        H: ConnHandler<I, O>,
    {
        let mut buf = BytesMut::with_capacity(
            17_usize // len(PROTOCOL) + len(instance) + len(func) + len([]) + len(params)
                .saturating_add(instance.len())
                .saturating_add(func.len())
                .saturating_add(params.len()),
        );
        buf.put_u8(PROTOCOL);
        CoreNameEncoder.encode(instance, &mut buf)?;
        CoreNameEncoder.encode(func, &mut buf)?;
        buf.put_u8(0);
        CoreVecEncoderBytes.encode(params, &mut buf)?;
        trace!(?buf, "writing invocation");
        tx.write_all(&buf)
            .await
            .context("failed to initialize connection")?;

        let Conn { tx, rx } = Conn::new::<H, _, _, _>(rx, tx, paths.as_ref());
        Ok((tx, rx))
    }
}

impl<H> Default for InvokeBuilder<H> {
    fn default() -> Self {
        Self(PhantomData)
    }
}

/// Invoke function `func` on instance `instance`
#[instrument(level = "trace", skip_all)]
pub async fn invoke<P, I, O>(
    tx: O,
    rx: I,
    instance: &str,
    func: &str,
    params: Bytes,
    paths: impl AsRef<[P]> + Send,
) -> anyhow::Result<(Outgoing, Incoming)>
where
    P: AsRef<[Option<usize>]> + Send + Sync,
    I: AsyncRead + Unpin + Send + 'static,
    O: AsyncWrite + Unpin + Send + 'static,
{
    InvokeBuilder::<()>::default()
        .invoke(tx, rx, instance, func, params, paths)
        .await
}