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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
//! Methods used to establish connections to DDMW Core servers' client
//! interfaces.
use futures::sink::SinkExt;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_stream::StreamExt;
use tokio_util::codec::Framed;
use blather::{codec, Telegram};
use crate::auth::Auth;
use crate::err::Error;
pub use protwrap::tokio::Stream;
pub use protwrap::ProtAddr;
pub type Frm = Framed<protwrap::tokio::Stream, blather::Codec>;
/// Connect to one of the DDMW core's client interfaces and optionally attempt
/// to authenticate.
///
/// If `ProtAddr::Tcp()` is passed into the `pa` argument an TCP/IP connection
/// will be attempted. If `ProtAddr::Uds()` (currently only available on
/// unix-like platforms) is used a unix local domain socket connection will be
/// attempted.
///
/// If `auth` has `Some` value, an authentication will be attempted after
/// successful connection. If the authentication fails the entire connection
/// will fail. To be able to keep the connection up in case the authentication
/// fails, pass `None` to the `auth` argument and manually authenticate in the
/// application.
///
/// # Examples
/// ```no_run
/// use ddmw_client::conn;
/// async fn test() {
/// let pa = protwrap::ProtAddr::Tcp("127.0.0.1:8777".to_string());
/// let conn = conn::connect(&pa, None).await.expect("Unable to connect");
/// }
/// ```
pub async fn connect(
pa: &ProtAddr,
auth: Option<&Auth>
) -> Result<Framed<protwrap::tokio::Stream, blather::Codec>, Error> {
let strm = protwrap::tokio::connect(pa).await?;
let mut framed = Framed::new(strm, blather::Codec::new());
if let Some(auth) = auth {
auth.authenticate(&mut framed).await?;
}
Ok(framed)
}
/// Send a telegram then wait for and return the server's reply.
/// If the server returns a `Fail`, it will be returned as
/// `Err(Error::ServerError)`.
pub async fn sendrecv<T>(
conn: &mut Framed<T, blather::Codec>,
tg: &Telegram
) -> Result<blather::Params, Error>
where
T: AsyncRead + AsyncWrite + Unpin
{
conn.send(tg).await?;
expect_okfail(conn).await
}
/// Waits for a message and ensures that it's Ok or Fail.
/// Converts Fail state to an Error::ServerError.
/// Returns a Params buffer containig the Ok parameters on success.
pub async fn expect_okfail<T>(
conn: &mut Framed<T, blather::Codec>
) -> Result<blather::Params, Error>
where
T: AsyncRead + Unpin
{
if let Some(o) = conn.next().await {
let o = o?;
match o {
codec::Input::Telegram(tg) => {
if let Some(topic) = tg.get_topic() {
if topic == "Ok" {
return Ok(tg.into_params());
} else if topic == "Fail" {
return Err(Error::ServerError(tg.into_params()));
}
}
}
_ => {
println!("unexpected reply");
}
}
return Err(Error::BadState("Unexpected reply from server.".to_string()));
}
Err(Error::Disconnected)
}
#[derive(Debug)]
pub struct WhoAmI {
pub id: i64,
pub name: String
}
/// Return the current owner of a connection.
///
/// # Examples
/// ```no_run
/// use ddmw_client::{conn, auth};
/// async fn test() {
/// let pa = protwrap::ProtAddr::Tcp("127.0.0.1:8777".to_string());
/// let auth = auth::Builder::new()
/// .name("elena")
/// .pass("secret")
/// .build().expect("Unable to build Auth buffer");
/// let mut frm = conn::connect(&pa, Some(&auth)).await
/// .expect("Connection failed");
///
/// let w = conn::whoami(&mut frm).await.expect("whoami failed");
/// assert_eq!(&w.name, "elena");
/// }
/// ```
pub async fn whoami<T>(
conn: &mut Framed<T, blather::Codec>
) -> Result<WhoAmI, Error>
where
T: AsyncRead + AsyncWrite + Unpin
{
let tg = Telegram::new_topic("WhoAmI")?;
let params = sendrecv(conn, &tg).await?;
let id = params.get_param::<i64>("Id")?;
let name = params.get_param("Name")?;
Ok(WhoAmI { id, name })
}
// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :