new msg macro

This commit is contained in:
2026-07-06 22:48:40 -04:00
parent 03df4470fd
commit a3b1416850
10 changed files with 260 additions and 197 deletions
+1 -1
Submodule iris updated: a648c62aa2...e817bc83af
+42 -27
View File
@@ -1,9 +1,9 @@
use crate::{Client, ClientEvent, ClientSender, Rsc, session::Session};
use crate::{Client, ClientSender, Rsc, session::Session};
use dashmap::DashMap;
use iris::prelude::DefaultRsc;
use openworm::net::{
ClientMsg, ClientMsgInst, RecvHandler, RequestId, RequestMsg, SERVER_NAME, ServerMsg,
ServerMsgInst, SkipServerVerification, recv_uni, send_uni,
ClientMsg, ClientMsgInst, RecvHandler, ReqErr, RequestId, RequestMsg, SERVER_NAME, ServerMsg,
ServerResp, SkipServerVerification, recv_uni, send_uni,
};
use quinn::{
ClientConfig, Connection, Endpoint, IdleTimeout, TransportConfig,
@@ -31,16 +31,16 @@ pub struct NetHandle {
}
type NetResult<T> = Result<T, String>;
type SyncReqFn = Box<dyn FnOnce(ServerMsg, &mut Rsc) + Send + Sync>;
type SyncReqFn = Box<dyn FnOnce(ServerResp, &mut Rsc) + Send + Sync>;
pub enum NetCtrlMsg {
Send(ClientMsg),
Request(ClientMsg, oneshot::Sender<ServerMsg>),
Request(ClientMsg, oneshot::Sender<ServerResp>),
RequestSync(ClientMsg, SyncReqFn),
Exit,
}
type Resp<R: RequestMsg> = Result<R::Result, ()>;
type Resp<R> = Result<<R as RequestMsg>::Result, Error>;
// TODO: move into iris?
pub trait MainDataCallback<Data, State>:
@@ -52,6 +52,12 @@ impl<F: FnOnce(Data, &mut DefaultRsc<State>) + Sync + Send + 'static, Data, Stat
{
}
pub enum Error {
Req(ReqErr),
InvalidResp,
Connection,
}
impl NetHandle {
fn send_(&self, msg: NetCtrlMsg) {
let _ = self.send.send(msg);
@@ -64,26 +70,32 @@ impl NetHandle {
pub async fn request<R: RequestMsg>(&self, msg: R) -> Resp<R> {
let (send, recv) = oneshot::channel();
self.send_(NetCtrlMsg::Request(msg.into(), send));
let Ok(recv) = recv.await else { return Err(()) };
if let Some(res) = R::result(recv) {
Ok(res)
} else {
Err(())
let Ok(recv) = recv.await else {
return Err(Error::Connection);
};
match recv {
ServerResp::Ok(resp) => match R::result(resp) {
Some(val) => Ok(val),
None => Err(Error::InvalidResp),
},
ServerResp::Err(err) => Err(Error::Req(err)),
}
}
pub fn request_sync<R: RequestMsg>(
&self,
msg: R,
callback: impl MainDataCallback<Result<R::Result, ()>, Client>,
callback: impl MainDataCallback<Result<R::Result, Error>, Client>,
) {
self.send_(NetCtrlMsg::RequestSync(
msg.into(),
Box::new(move |msg, rsc| {
let res = if let Some(res) = R::result(msg) {
Ok(res)
} else {
Err(())
let res = match msg {
ServerResp::Ok(resp) => match R::result(resp) {
Some(val) => Ok(val),
None => Err(Error::InvalidResp),
},
ServerResp::Err(err) => Err(Error::Req(err)),
};
callback(res, rsc);
}),
@@ -104,7 +116,7 @@ impl<R: RequestMsg> SyncRecv<R> {
match self.recv.try_recv() {
Ok(res) => Some(res),
Err(oneshot::error::TryRecvError::Empty) => None,
Err(oneshot::error::TryRecvError::Closed) => Some(Err(())),
Err(oneshot::error::TryRecvError::Closed) => Some(Err(Error::Connection)),
}
}
}
@@ -252,22 +264,25 @@ where
}
struct ServerRecv {
requests: DashMap<RequestId, oneshot::Sender<ServerMsg>>,
requests: DashMap<RequestId, oneshot::Sender<ServerResp>>,
requests_sync: DashMap<RequestId, SyncReqFn>,
event_sender: ClientSender,
id: usize,
}
impl RecvHandler<ServerMsgInst> for ServerRecv {
async fn msg(&self, resp: ServerMsgInst) {
if let Some(id) = resp.id {
if let Some((_, send)) = self.requests.remove(&id) {
let _ = send.send(resp.msg);
} else if let Some((_, f)) = self.requests_sync.remove(&id) {
self.event_sender.run(|rsc| f(resp.msg, rsc));
impl RecvHandler<ServerMsg> for ServerRecv {
async fn msg(&self, resp: ServerMsg) {
match resp {
ServerMsg::Response { id, msg } => {
if let Some((_, send)) = self.requests.remove(&id) {
let _ = send.send(msg);
} else if let Some((_, f)) = self.requests_sync.remove(&id) {
self.event_sender.run(|rsc| f(msg, rsc));
}
}
ServerMsg::Notif(notif) => {
Session::handle(&self.event_sender, self.id, notif);
}
} else {
Session::handle(&self.event_sender, self.id, resp);
}
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex, MutexGuard};
use iris::prelude::*;
use openworm::net::{ServerMsgInst, UserId};
use openworm::net::{ServerNotif, UserId};
use crate::{ClientSender, Rsc, net::NetHandle, ui::UserCache};
@@ -39,8 +39,8 @@ impl Session {
span
}
pub fn handle(sender: &ClientSender, id: usize, inst: ServerMsgInst) {
match inst.msg {
pub fn handle(sender: &ClientSender, id: usize, notif: ServerNotif) {
match notif {
_ => {
println!("Unknown server message recieved");
}
+1
View File
@@ -32,6 +32,7 @@ impl Session {
s.con
.request_sync(RequestUserInfo { id }, move |resp, rsc| {
if let Ok(info) = resp {
let info = info.info;
let s = &mut session.get().cache;
for &widget in s.widgets.get(&id).into_iter().flatten() {
*rsc[widget].content = info.username.clone();
+10 -8
View File
@@ -41,7 +41,7 @@ impl ClientHandler {
if let ClientState::Authed(uid) = &*self.state.read().await {
Some(*uid)
} else {
reply!(NotLoggedIn);
reply!(ReqErr::NotLoggedIn);
}
};
let check_user = async || {
@@ -49,10 +49,10 @@ impl ClientHandler {
if let Some(user) = self.db.users.get(uid) {
Some((*uid, user))
} else {
reply!(InvalidUser);
reply!(ReqErr::InvalidUser);
}
} else {
reply!(NotLoggedIn);
reply!(ReqErr::NotLoggedIn);
}
};
let check_server_perms = async |perms| {
@@ -63,7 +63,7 @@ impl ClientHandler {
.get(&id)
.is_some_and(|u| !u.server_perms.contains(perms))
{
reply!(NoPermission);
reply!(ReqErr::NoPermission);
} else {
Some(())
}
@@ -172,7 +172,7 @@ impl ClientHandler {
loop {
let mut tx = db.write_tx();
let Some(mut user) = tx.get(&db.users, &user_id) else {
reply!(InvalidUser);
reply!(ReqErr::InvalidUser);
};
let Some(other_id) = tx.get(&db.usernames, &info.username) else {
reply!(AddFriendResp::UnknownUser);
@@ -265,11 +265,13 @@ impl ClientHandler {
ClientMsg::RequestUserInfo(info) => {
check_user().await?;
let Some(other_user) = db.users.get(&info.id) else {
reply!(InvalidUser);
reply!(ReqErr::InvalidUser);
};
// TODO: check perms... (privacy settings)
reply!(UserInfo {
username: other_user.username
reply!(RequestUserInfoResp {
info: UserInfo {
username: other_user.username
}
});
}
}
+6 -10
View File
@@ -1,6 +1,6 @@
use openworm::net::{
ClientMsgInst, RecvHandler, RequestId, SERVER_NAME, SendResult, ServerMsg, ServerMsgInst,
recv_uni, send_uni,
ClientMsgInst, RecvHandler, RequestId, ServerResp, SERVER_NAME, SendResult, ServerMsg, recv_uni,
send_uni,
};
use quinn::{
Connection, Endpoint, ServerConfig,
@@ -65,11 +65,7 @@ impl ClientSender {
}
pub async fn send(&self, msg: impl Into<ServerMsg>) -> SendResult {
let msg = ServerMsgInst {
id: None,
msg: msg.into(),
};
send_uni(&self.conn, msg).await
send_uni(&self.conn, msg.into()).await
}
}
@@ -79,9 +75,9 @@ pub struct ClientReplier {
}
impl ClientReplier {
pub async fn send(&self, msg: impl Into<ServerMsg>) {
let msg = ServerMsgInst {
id: Some(self.req_id),
pub async fn send(&self, msg: impl Into<ServerResp>) {
let msg = ServerMsg::Response {
id: self.req_id,
msg: msg.into(),
};
let _ = send_uni(&self.conn, msg).await;
+2
View File
@@ -1,2 +1,4 @@
#![feature(macro_metavar_expr_concat)]
pub mod net;
pub mod rsc;
-100
View File
@@ -1,22 +1,3 @@
use iris::core::util::HashSet;
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct CreateChannel {
pub name: String,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum CreateChannelResp {
Ok,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct RequestUserInfo {
pub id: UserId,
}
pub type RequestUserInfoResp = UserInfo;
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct UserInfo {
pub username: String,
@@ -25,92 +6,18 @@ pub struct UserInfo {
pub type UserId = u64;
pub type AccountToken = String;
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct CreateAccount {
pub username: String,
pub password: String,
pub token: String,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum CreateAccountResp {
Ok { id: UserId },
UsernameExists,
InvalidToken,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct Login {
pub username: String,
pub password: String,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum LoginResp {
Ok { id: UserId },
UnknownUsername,
InvalidPassword,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct RequestUsers;
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct RequestUsersResp {
pub users: Vec<ServerUser>,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct ServerUser {
pub id: UserId,
pub username: String,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct RequestFriends;
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct RequestFriendsResp {
pub current: HashSet<UserId>,
pub incoming: HashSet<UserId>,
pub outgoing: HashSet<UserId>,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct AddFriend {
pub username: String,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum AddFriendResp {
Ok,
UnknownUser,
CannotAddSelf,
AlreadySent,
AlreadyFriends,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct RemoveFriend {
pub id: UserId,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct AnswerFriendRequest {
pub id: UserId,
pub action: FriendRequestAction,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum FriendRequestAction {
Accept,
Deny,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct GenerateToken {
pub perms: ServerPerms,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct GenerateTokenResp {
pub token: AccountToken,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
pub struct ServerPerms(u32);
impl ServerPerms {
@@ -123,10 +30,3 @@ impl ServerPerms {
(self.0 & other.0) == other.0
}
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct NotLoggedIn;
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct NoPermission;
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct InvalidUser;
+194 -41
View File
@@ -1,57 +1,210 @@
use crate::net::RequestId;
use super::{RequestMsg, data::*};
use iris::core::util::HashSet;
msg_type!(ClientMsg: {
0: CreateAccount => CreateAccountResp,
1: Login => LoginResp,
2: RequestUsers => RequestUsersResp,
3: RequestFriends => RequestFriendsResp,
4: AddFriend => AddFriendResp,
5: RemoveFriend,
6: AnswerFriendRequest,
7: GenerateToken => GenerateTokenResp,
8: RequestUserInfo => RequestUserInfoResp,
9: CreateChannel => CreateChannelResp,
});
msg_type!(ServerMsg: {
0: NotLoggedIn,
1: NoPermission,
2: InvalidUser,
3: CreateAccountResp,
4: LoginResp,
5: RequestUsersResp,
6: RequestFriendsResp,
7: AddFriendResp,
8: GenerateTokenResp,
9: RequestUserInfoResp,
10: CreateChannelResp,
});
def_msgs! {
ClientMsg => ClientMsgResp {
0: CreateAccount {
username: String,
password: String,
token: String,
} => enum {
Ok { id: UserId },
UsernameExists,
InvalidToken,
},
1: Login {
username: String,
password: String,
} => enum {
Ok { id: UserId },
UnknownUsername,
InvalidPassword,
},
2: RequestUsers; => struct {
users: Vec<ServerUser>,
},
3: RequestFriends; => struct {
current: HashSet<UserId>,
incoming: HashSet<UserId>,
outgoing: HashSet<UserId>,
},
4: AddFriend {
username: String,
} => enum {
Ok,
UnknownUser,
CannotAddSelf,
AlreadySent,
AlreadyFriends,
},
5: RemoveFriend {
id: UserId,
} => struct;,
6: AnswerFriendRequest {
id: UserId,
action: FriendRequestAction,
} => struct;,
7: GenerateToken {
perms: ServerPerms,
} => struct {
token: AccountToken,
},
8: RequestUserInfo {
id: UserId,
} => struct {
info: UserInfo,
},
9: CreateChannel {
name: String,
} => enum {
Ok
},
}
ServerNotif {
0: struct Dummy;,
}
RequestMsg, ServerResp
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum ServerResp {
Ok(ClientMsgResp),
Err(ReqErr),
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum ReqErr {
UnknownRequest,
NotLoggedIn,
NoPermission,
InvalidUser,
}
#[repr(u32)]
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum ServerMsg {
Response { id: RequestId, msg: ServerResp } = 0,
Notif(ServerNotif),
}
impl From<ReqErr> for ServerResp {
fn from(value: ReqErr) -> Self {
Self::Err(value)
}
}
macro_rules! def_msgs {
(
$c_msg:ident => $s_resp:ident {
$($c_num:literal: $c_name:ident $c_def:tt => $c_resp_kind:tt $c_resp_def:tt,)*
}
$s_notif:ident {
$($s_num:literal: $s_kind:tt $s_name:ident $s_def:tt,)*
}
$req_t:ident, $resp:ident
) => {
msg_type! { $c_msg;
$($c_num: struct $c_name $c_def,)*
}
msg_type! { $s_resp;
$($c_num: $c_resp_kind ${concat($c_name, Resp)} $c_resp_def,)*
}
msg_type! { $s_notif;
$($s_num: $s_kind $s_name $s_def,)*
}
macro_rules! msg_type {
($msg:ident: {$($num:literal: $name:ident $(=> $resp:ident)?,)*}) => {
#[repr(u32)]
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum $msg {$(
$name($name) = $num,
)*}
$(
impl From<$name> for $msg {
fn from(value: $name) -> Self {
Self::$name(value)
impl From<${concat($c_name, Resp)}> for $resp {
fn from(value: ${concat($c_name, Resp)}) -> Self {
Self::Ok(value.into())
}
}
$(
impl RequestMsg for $name {
type Result = $resp;
fn result(msg: ServerMsg) -> Option<Self::Result> {
if let ServerMsg::$resp(res) = msg {
impl $req_t for $c_name {
type Result = ${concat($c_name, Resp)};
fn result(msg: $s_resp) -> Option<Self::Result> {
if let $s_resp::${concat($c_name, Resp)}(res) = msg {
Some(res)
} else {
None
}
}
}
)?
)*
};
}
use def_msgs;
macro_rules! msg_type {
(
@finish
$msg:ident {$($num:literal: $name:ident,)*}
) => {
#[repr(u32)]
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub enum $msg {
$(
$name($name) = $num,
)*
}
$(
impl From<$name> for $msg {
fn from(value: $name) -> Self {
Self::$name(value)
}
}
)*
};
(
@derive $item:item
) => {
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
$item
};
(
@def struct $name:ident {$($field:ident: $ty:ty,)*}
) => {
msg_type!(@derive pub struct $name {$(pub $field: $ty,)*});
};
(
@def struct $name:ident ($($ty:ty $(,)?)*)
) => {
msg_type!(@derive pub struct $name ($(pub $ty, )*););
};
(
@def struct $name:ident;
) => {
msg_type!(@derive pub struct $name;);
};
(
@def enum $name:ident $rest:tt
) => {
msg_type!(@derive pub enum $name $rest);
};
(
$msg:ident;
$($num:literal: $kind:tt $name:tt $def:tt,)*
) => {
$(msg_type!(@def $kind $name $def);)*
msg_type!(@finish $msg {$($num: $name,)*});
};
}
use msg_type;
+1 -7
View File
@@ -24,13 +24,7 @@ pub struct ClientMsgInst {
pub msg: ClientMsg,
}
#[derive(Debug, bitcode::Encode, bitcode::Decode)]
pub struct ServerMsgInst {
pub id: Option<RequestId>,
pub msg: ServerMsg,
}
pub trait RequestMsg: Into<ClientMsg> {
type Result: Send + Sync + 'static;
fn result(msg: ServerMsg) -> Option<Self::Result>;
fn result(msg: ClientMsgResp) -> Option<Self::Result>;
}