use std::sync::Arc;
use thunder::server::{
spawn_listener, AuthError, Credentials, Dispatch, ListenerConfig, Principal, ServerInfo,
Session,
};
use thunder::{Client, ClientConfig, Config, Value};
struct Echo;
impl Dispatch for Echo {
type Identity = ();
async fn dispatch(
&self,
_session: &Session,
command: &str,
mut args: Vec<Value>,
) -> Result<Value, String> {
match command {
"PING" => Ok(Value::Str("PONG".to_owned())),
"ECHO" if !args.is_empty() => Ok(args.swap_remove(0)),
other => Err(format!("ERR unknown command '{other}'")),
}
}
async fn authenticate(&self, _creds: Credentials) -> Result<Principal, AuthError> {
Ok(Principal::new("hello".to_owned()))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = Config::standard().scheme("hello").port(0);
let server = spawn_listener(
Arc::new(Echo),
app.clone(),
ServerInfo {
name: "hello-server".to_owned(),
version: env!("CARGO_PKG_VERSION").to_owned(),
},
ListenerConfig::default().open(),
)
.await?;
let addr = server.local_addr().to_string();
println!("server listening on {addr}");
let client =
Client::connect_with(&addr, app, ClientConfig::new().client_name("hello-client")).await?;
println!(
"client connected (handshake done, authenticated = {})",
client.is_authenticated()
);
let pong = client.call("PING", vec![]).await?;
println!("PING -> {:?}", pong.as_str());
let echo = client
.call("ECHO", vec![Value::Str("hello, thunder".to_owned())])
.await?;
println!("ECHO -> {:?}", echo.as_str());
match client.call("NOPE", vec![]).await {
Ok(v) => println!("NOPE -> unexpected ok: {v:?}"),
Err(e) => println!("NOPE -> typed error (as designed): {e}"),
}
client.close().await;
server.stop().await;
println!("done.");
Ok(())
}