merge develop into master #20

Merged
michael-bailey merged 181 commits from develop into master 2023-12-01 21:48:28 +00:00
10 changed files with 0 additions and 1051 deletions
Showing only changes of commit eb8a512c04 - Show all commits

View File

@ -20,7 +20,6 @@ use actix::Handler;
use crate::client_management::ClientManagerMessage;
use foundation::messages::network::NetworkSockOut;
use foundation::ClientDetails;
use server::Server;
use crate::network::{NetworkManager, NetworkMessage};
/// This struct is the main actor of the server.

View File

@ -1,80 +0,0 @@
use crate::client::Client;
use crate::messages::ServerMessage;
use std::sync::{Arc, Weak};
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::Mutex;
#[derive(Clone, Debug)]
pub struct Message {
content: String,
sender: Weak<Client<>>,
}
impl Message {
#[allow(unused)]
pub fn new(content: String, sender: Weak<Client<>>) -> Message {
Message { content, sender }
}
}
enum ChatManagerMessage {
AddMessage {sender: Weak<Client<>>, content: String}
}
pub struct ChatManager {
messages: Mutex<Vec<Message>>,
server_channel: Sender<ServerMessage>,
#[allow(unused)]
tx: Sender<ChatManagerMessage>,
rx: Mutex<Receiver<ChatManagerMessage>>,
}
impl ChatManager {
#[allow(unused)]
pub fn new(server_channel: Sender<ServerMessage>) -> Arc<Self> {
let (tx, rx) = channel::<ChatManagerMessage>(1024);
let manager = Arc::new(ChatManager {
messages: Mutex::new(Vec::new()),
server_channel,
tx,
rx: Mutex::new(rx),
});
manager.start();
manager
}
#[allow(unused)]
fn start(self: &Arc<ChatManager>) {
let manager = self.clone();
tokio::spawn(async move {
use ServerMessage::{BroadcastGlobalMessage};
use ChatManagerMessage::{AddMessage};
while let Some(message) = manager.rx.lock().await.recv().await {
match message {
AddMessage { content,sender } => {
let sender = &sender.upgrade().unwrap().details.uuid;
manager.server_channel.send(
BroadcastGlobalMessage {sender: sender.clone(), content}
).await.unwrap();
}
}
}
});
}
#[allow(unused)]
pub async fn add_message(self: &Arc<Self>, sender: Weak<Client>, content: String) {
let mut a = self.messages.lock().await;
a.push(Message::new(content, sender))
}
#[allow(unused)]
pub async fn get_all_messages(self: &Arc<Self>) -> Vec<Message> {
self.messages.lock().await.clone()
}
}

View File

@ -1,247 +0,0 @@
use std::cmp::Ordering;
use std::io::Error;
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use async_trait::async_trait;
use mlua::prelude::LuaUserData;
use mlua::{UserDataFields, UserDataMethods};
use tokio::select;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use foundation::messages::client::{ClientStreamIn, ClientStreamOut};
use foundation::ClientDetails;
use foundation::connection::Connection;
use foundation::messages::client::ClientStreamOut::{Connected, Disconnected};
use foundation::prelude::IManager;
use crate::messages::{ClientMessage};
/// # ClientInMessage
///
/// Messages that are sent internally
/// when functions are called on the client
#[derive(Serialize, Deserialize)]
enum ClientInMessage {
MessageTo,
UpdateRequest,
}
/// # Client
/// This struct represents a connected user.
///
/// ## Attributes
/// - details: store of the clients infomation.
///
/// - stream: The socket for the connected client.
/// - stream_reader: the buffered reader used to receive messages
/// - stream_writer: the buffered writer used to send messages
/// - owner: An optional reference to the owning object.
#[derive(Debug)]
pub struct Client<Out: 'static>
where
Out: From<ClientMessage> + Send
{
pub details: ClientDetails,
out_channel: Sender<Out>,
connection: Arc<Connection>,
}
impl<Out> Client<Out>
where
Out: From<ClientMessage> + Send {
pub fn new(
uuid: Uuid,
username: String,
address: String,
out_channel: Sender<Out>,
connection: Arc<Connection>
) -> Arc<Client<Out>> {
Arc::new(Client {
details: ClientDetails {
uuid,
username,
address: address.to_string(),
public_key: None,
},
connection,
out_channel,
})
}
async fn handle_connection(&self, value: Result<ClientStreamIn, Error>) {
match value {
Ok(ClientStreamIn::Disconnect) => {
println!(
"[Client {:?}]: Disconnect received",
self.details.uuid
);
self.disconnect().await;
return;
}
Ok(ClientStreamIn::SendMessage { to, content }) => {
let _ = self.out_channel.send(
ClientMessage::IncomingMessage {from: self.details.uuid, to, content}.into()
).await;
}
Ok(ClientStreamIn::SendGlobalMessage { content }) => {
let _ = self.out_channel.send(
ClientMessage::IncomingGlobalMessage {from: self.details.uuid, content}.into()
).await;
}
_ => {
self.error("Command not found").await;
}
}
}
pub async fn broadcast_message(&self, from: Uuid, content: String) -> Result<(), Error> {
self.connection.write(ClientStreamOut::GlobalMessage { from, content }).await?;
Ok(())
}
pub async fn user_message(&self, from: Uuid, content: String) -> Result<(), Error> {
self.connection.write(ClientStreamOut::UserMessage { from, content }).await?;
Ok(())
}
async fn disconnect(&self) {
let _ = self.out_channel
.send(ClientMessage::Disconnect {
id: self.details.uuid,
}.into()).await;
}
async fn error(&self, msg: &str) {
let _ = self.connection.write(ClientStreamOut::Error).await;
}
}
#[async_trait]
impl<Out> IManager for Client<Out>
where
Out: From<ClientMessage> + Send
{
async fn init(self: &Arc<Self>)
where
Self: Send + Sync + 'static
{
let _ = self.connection.write(Connected).await;
}
async fn run(self: &Arc<Self>) {
let client = self.clone();
select! {
val = self.connection.read::<ClientStreamIn>() => {
tokio::spawn(async move {
client.handle_connection(val).await;
});
}
}
}
}
// MARK: - use to handle disconnecting
impl<Out> Drop for Client<Out>
where
Out: From<ClientMessage> + Send
{
fn drop(&mut self) {
let connection = self.connection.clone();
let id = self.details.uuid.clone();
tokio::spawn(async move {
let _ = connection.write(Disconnected).await;
});
}
}
// MARK: - used for sorting.
impl<Out> PartialEq for Client<Out>
where
Out: From<ClientMessage> + Send
{
fn eq(&self, other: &Self) -> bool {
self.details.uuid == other.details.uuid
}
}
impl<Out> Eq for Client<Out>
where
Out: From<ClientMessage> + Send
{}
impl<Out> PartialOrd for Client<Out>
where
Out: From<ClientMessage> + Send
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<Out: 'static> Ord for Client<Out>
where
Out: From<ClientMessage> + Send
{
fn cmp(&self, other: &Self) -> Ordering {
self.details.uuid.cmp(&other.details.uuid)
}
}
#[cfg(test)]
mod test {
use std::io::Error;
use tokio::sync::mpsc::channel;
use uuid::Uuid;
use foundation::connection::Connection;
use foundation::messages::client::ClientStreamOut;
use foundation::messages::client::ClientStreamOut::{Connected, Disconnected};
use foundation::prelude::IManager;
use foundation::test::create_connection_pair;
use crate::client::{Client};
use crate::messages::ClientMessage;
use crate::messages::ClientMessage::Disconnect;
#[tokio::test]
async fn create_client_and_drop() -> Result<(), Error> {
let (sender, mut receiver) =
channel::<ClientMessage>(1024);
let (server, (client_conn, addr)) =
create_connection_pair().await?;
// client details
let uuid = Uuid::new_v4();
let username = "TestUser".to_string();
let client = Client::new(
uuid,
username,
addr.to_string(),
sender.clone(),
server
);
client.start();
let res = client_conn.read::<ClientStreamOut>().await?;
assert_eq!(res, Connected);
drop(client);
let res = client_conn.read::<ClientStreamOut>().await?;
assert_eq!(res, Disconnected);
// fetch from out_channel
let disconnect_msg = receiver.recv().await.unwrap();
assert_eq!(disconnect_msg, Disconnect {id: uuid});
Ok(())
}
}

View File

@ -1,209 +0,0 @@
use std::collections::HashMap;
use std::sync::Arc;
use futures::future::join_all;
use tokio::select;
use tokio::sync::Mutex;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use uuid::Uuid;
use async_trait::async_trait;
use foundation::connection::Connection;
use foundation::prelude::IManager;
use crate::client::Client;
use crate::messages::ClientMessage;
#[derive(Debug)]
pub enum ClientMgrMessage {
#[allow(dead_code)]
Remove {
id: Uuid,
},
#[allow(dead_code)]
SendClients {
to: Uuid,
},
SendMessage {
from: Uuid,
to: Uuid,
content: String,
},
BroadcastGlobalMessage {
from: Uuid,
content: String,
},
}
impl From<ClientMessage> for ClientMgrMessage {
fn from(msg: ClientMessage) -> Self {
use ClientMessage::{Disconnect, IncomingGlobalMessage, IncomingMessage};
match msg {
IncomingMessage { from, to, content } => ClientMgrMessage::SendMessage { from, to, content },
IncomingGlobalMessage { from, content } => {
ClientMgrMessage::BroadcastGlobalMessage { from, content }
}
Disconnect { id } => ClientMgrMessage::Remove { id },
_ => unimplemented!(),
}
}
}
/// # ClientManager
/// This struct manages all users connected to the server.
///
/// ## Attributes
/// - clients: a vector of all clients being managed.
/// - server_channel: a channel to the parent that manages this object.
/// - tx: the sender that clients will send their messages to.
/// - rx: the receiver where messages are sent to.
pub struct ClientManager<Out: 'static>
where
Out: From<ClientMgrMessage> + Send,
{
pub clients: Mutex<HashMap<Uuid, Arc<Client<ClientMgrMessage>>>>,
#[allow(dead_code)]
server_channel: Mutex<Sender<Out>>,
tx: Sender<ClientMgrMessage>,
rx: Mutex<Receiver<ClientMgrMessage>>,
}
impl<Out> ClientManager<Out>
where
Out: From<ClientMgrMessage> + Send,
{
pub fn new(out_channel: Sender<Out>) -> Arc<Self> {
let (tx, rx) = channel(1024);
Arc::new(ClientManager {
clients: Mutex::default(),
server_channel: Mutex::new(out_channel),
tx,
rx: Mutex::new(rx),
})
}
#[allow(dead_code)]
pub async fn get_count(&self) -> usize {
self.clients.lock().await.len()
}
pub async fn add_client(
&self,
id: Uuid,
username: String,
address: String,
connection: Arc<Connection>,
) {
let client = Client::new(id, username, address, self.tx.clone(), connection);
client.start();
let mut lock = self.clients.lock().await;
lock.insert(client.details.uuid, client);
}
#[allow(dead_code)]
pub async fn remove_client(&self, id: Uuid) {
let mut lock = self.clients.lock().await;
lock.remove(&id);
}
pub async fn handle_channel(&self, message: Option<ClientMgrMessage>) {
use ClientMgrMessage::{BroadcastGlobalMessage, Remove, SendClients, SendMessage};
println!("Handling channel");
match message {
Some(Remove { id }) => {
println!("[Client Manager]: removing client: {:?}", &id);
let mut lock = self.clients.lock().await;
lock.remove(&id);
}
Some(SendClients { to: _ }) => {
let lock = self.clients.lock().await;
let futures = lock
.iter()
.map(|(_, _)| async { println!("Send message to Client") });
join_all(futures).await;
}
Some(BroadcastGlobalMessage { from, content }) => {
let lock = self.clients.lock().await;
let futures =
lock
.iter()
.map(|(_, c)| (c.clone(), content.clone()))
.map(|(c, s)| async move {
c.broadcast_message(from, s).await.unwrap();
});
join_all(futures).await;
}
Some(SendMessage { from, to, content }) => {
let lock = self.clients.lock().await;
let client = lock.get(&to).unwrap();
let _ = client.user_message(from, content).await;
}
Some(Remove { id }) => {
self.clients.lock().await.remove(&id);
}
_ => {
unimplemented!()
}
}
}
}
#[async_trait]
impl<Out> IManager for ClientManager<Out>
where
Out: From<ClientMgrMessage> + Send,
{
async fn run(self: &Arc<Self>) {
loop {
let mut receiver = self.rx.lock().await;
select! {
val = receiver.recv() => {
self.handle_channel(val).await;
}
}
}
}
}
#[cfg(test)]
mod test {
use crate::client_manager::{ClientManager, ClientMgrMessage};
use foundation::messages::client::ClientStreamOut;
use foundation::prelude::IManager;
use foundation::test::create_connection_pair;
use std::io::Error;
use tokio::sync::mpsc::channel;
use uuid::Uuid;
#[tokio::test]
async fn add_new_client_to_manager() -> Result<(), Error> {
let (sender, mut receiver) = channel::<ClientMgrMessage>(1024);
let (server, (client, addr)) = create_connection_pair().await?;
let client_manager = ClientManager::new(sender);
client_manager.start();
let id = Uuid::new_v4();
let username = "TestUser".to_string();
client_manager
.add_client(id, username.clone(), addr.to_string(), server)
.await;
assert_eq!(client_manager.get_count().await, 1);
let msg = client.read::<ClientStreamOut>().await?;
assert_eq!(msg, ClientStreamOut::Connected);
Ok(())
}
}

View File

@ -1,10 +0,0 @@
use crate::client::Client;
use std::sync::Arc;
use uuid::Uuid;
pub enum EventType<'a> {
NewConnection,
// Todo: - change client to use traits
ClientAdded(Uuid),
Custom(&'a str),
}

View File

@ -1,13 +0,0 @@
// mod chat_manager;
mod client;
mod client_manager;
mod event_type;
mod lua;
mod messages;
mod network;
mod network_manager;
// pub mod plugin;
mod prelude;
mod server;
pub use server::Server;

View File

@ -1,25 +0,0 @@
// pub mod chat_manager;
pub mod client;
pub mod client_manager;
mod event_type;
mod lua;
pub mod messages;
mod network;
pub mod network_manager;
// mod plugin;
mod prelude;
pub mod server;
use std::io;
use clap::{App, Arg};
use server::Server;
#[tokio::main]
async fn main() -> io::Result<()> {
let server = Server::new().await.unwrap();
server.start().await;
Ok(())
}

View File

@ -1,52 +0,0 @@
use uuid::Uuid;
/// # ClientMessage
///
/// These messages are send from the client to a receiver
/// when events from the client happen that need to be delegated
///
/// ## Variants
///
///
/// ## Methods
///
#[derive(Debug)]
pub enum ClientMessage {
#[allow(dead_code)]
Connected,
#[allow(dead_code)]
IncomingMessage {
from: Uuid,
to: Uuid,
content: String,
},
#[allow(dead_code)]
IncomingGlobalMessage {
from: Uuid,
content: String,
},
#[allow(dead_code)]
RequestedUpdate {
from: Uuid,
},
Disconnect {
id: Uuid,
},
Error,
}
impl PartialEq for ClientMessage {
fn eq(&self, other: &Self) -> bool {
use ClientMessage::{Connected, Disconnect, Error};
match (self, other) {
(Connected, Connected) => true,
(Error, Error) => true,
(Disconnect { id, .. }, Disconnect { id: other_id, .. }) => id == other_id,
_ => false,
}
}
}

View File

@ -1,257 +0,0 @@
use std::io::{Error, ErrorKind};
use std::sync::Arc;
use uuid::Uuid;
use async_trait::async_trait;
use tokio::net::TcpListener;
use tokio::select;
use tokio::sync::mpsc::Sender;
use tokio::sync::Mutex;
use foundation::connection::Connection;
use foundation::messages::network::{NetworkSockIn, NetworkSockOut};
use foundation::prelude::IManager;
#[derive(Debug)]
pub enum NetworkManagerMessage {
ClientConnecting {
uuid: Uuid,
address: String,
username: String,
connection: Arc<Connection>,
},
}
impl PartialEq for NetworkManagerMessage {
fn eq(&self, other: &Self) -> bool {
use NetworkManagerMessage::ClientConnecting;
match (self, other) {
(
ClientConnecting {
uuid,
address,
username,
..
},
ClientConnecting {
uuid: other_uuid,
address: other_address,
username: other_username,
..
},
) => uuid == other_uuid && address == other_address && username == other_username,
#[allow(unreachable_patterns)]
_ => false,
}
}
}
/// # NetworkManager
///
/// This handles all new incoming connections to the server, involved with the chat services.
///
/// ## Fields
/// - address: the socket address that the server is listening on.
/// - listener: the TcpListener that is receiving connections.
/// - out_channel: the channel that will be sent events from NetworkManager.
pub struct NetworkManager<Out>
where
Out: From<NetworkManagerMessage> + Send,
{
listener: Mutex<TcpListener>,
out_channel: Sender<Out>,
}
impl<Out> NetworkManager<Out>
where
Out: From<NetworkManagerMessage> + Send,
{
pub async fn new(
address: &str,
out_channel: Sender<Out>,
) -> Result<Arc<NetworkManager<Out>>, Error> {
let listener = TcpListener::bind(address).await?;
Ok(Arc::new(NetworkManager {
listener: Mutex::new(listener),
out_channel,
}))
}
/// This fetches the port from the NetworkManager
pub async fn port(&self) -> u16 {
self.listener.lock().await.local_addr().unwrap().port()
}
/// This fetches the IP address from the NetworkManager
#[allow(dead_code)]
pub async fn address(&self) -> String {
self
.listener
.lock()
.await
.local_addr()
.unwrap()
.ip()
.to_string()
}
async fn handle_connection(&self, connection: Arc<Connection>) -> Result<(), Error> {
use NetworkSockIn::{Connect, Info};
use NetworkSockOut::{Connecting, GotInfo, Request};
connection.write(Request).await?;
match connection.read().await? {
Info => {
connection
.write(GotInfo {
server_name: "TestServer".into(),
server_owner: "Michael".into(),
})
.await?
}
Connect {
uuid,
address,
username,
} => {
connection.write(Connecting).await?;
let _ = self
.out_channel
.send(
NetworkManagerMessage::ClientConnecting {
uuid,
address,
username,
connection,
}
.into(),
)
.await;
}
#[allow(unreachable_patterns)]
_ => {
return Err(Error::new(
ErrorKind::InvalidData,
"Did not receive valid message",
));
}
}
Ok(())
}
}
#[async_trait]
impl<Out: 'static> IManager for NetworkManager<Out>
where
Out: From<NetworkManagerMessage> + Send,
{
async fn run(self: &Arc<Self>) {
let lock = self.listener.lock().await;
select! {
val = lock.accept() => {
if let Ok((stream, _addr)) = val {
let conn = self.clone();
tokio::spawn(async move {
let _ = conn.handle_connection(Arc::new(stream.into())).await;
});
}
}
}
}
}
#[cfg(test)]
mod test {
use crate::network_manager::{
NetworkManager, NetworkManagerMessage, NetworkManagerMessage::ClientConnecting,
};
use foundation::connection::Connection;
use foundation::messages::network::NetworkSockIn::{Connect, Info};
use foundation::messages::network::NetworkSockOut;
use foundation::messages::network::NetworkSockOut::{Connecting, GotInfo, Request};
use foundation::prelude::IManager;
use std::io::Error;
use tokio::sync::mpsc::channel;
use uuid::Uuid;
#[tokio::test]
async fn test_network_fetch_info() -> Result<(), Error> {
let (tx, _rx) = channel::<NetworkManagerMessage>(16);
let network_manager = NetworkManager::new("localhost:0", tx).await?;
network_manager.start();
let port = network_manager.port().await;
let client = Connection::new();
client.connect(format!("localhost:{}", port)).await?;
assert_eq!(client.read::<NetworkSockOut>().await?, Request);
client.write(Info).await?;
let out = client.read::<NetworkSockOut>().await?;
assert_eq!(
out,
GotInfo {
server_owner: "Michael".into(),
server_name: "TestServer".into()
}
);
Ok(())
}
#[tokio::test]
async fn test_network_login() -> Result<(), Error> {
let (tx, mut rx) = channel::<NetworkManagerMessage>(16);
let network_manager = NetworkManager::new("localhost:0", tx).await?;
network_manager.start();
let port = network_manager.port().await;
let client = Connection::new();
client.connect(format!("localhost:{}", port)).await?;
assert_eq!(client.read::<NetworkSockOut>().await?, Request);
// construct client data
let uuid = Uuid::new_v4();
let address = "localhost";
let username = "TestUser";
client
.write(Connect {
uuid,
address: address.to_string(),
username: username.to_string(),
})
.await?;
let res: NetworkSockOut = client.read().await?;
assert_eq!(res, Connecting);
let network_out = rx.recv().await.unwrap();
assert_eq!(
network_out,
ClientConnecting {
uuid,
address: address.to_string(),
username: username.to_string(),
connection: client
}
);
Ok(())
}
}

View File

@ -1,157 +0,0 @@
use foundation::connection::Connection;
use std::io::Error;
use std::sync::Arc;
use uuid::Uuid;
use tokio::sync::{
mpsc::{channel, Receiver},
Mutex,
};
// use crate::plugin::{PluginManager, PluginManagerMessage};
use crate::{
client_manager::{ClientManager, ClientMgrMessage},
network_manager::{NetworkManager, NetworkManagerMessage},
};
use foundation::prelude::IManager;
#[derive(Debug, Clone)]
pub enum ServerMessage {
ClientConnected {
uuid: Uuid,
address: String,
username: String,
connection: Arc<Connection>,
},
BroadcastGlobalMessage {
from: Uuid,
content: String,
},
}
impl From<NetworkManagerMessage> for ServerMessage {
fn from(msg: NetworkManagerMessage) -> Self {
use NetworkManagerMessage::ClientConnecting;
match msg {
ClientConnecting {
uuid,
address,
username,
connection,
} => ServerMessage::ClientConnected {
uuid,
address,
username,
connection,
},
#[allow(unreachable_patterns)]
_ => unimplemented!(),
}
}
}
impl From<ClientMgrMessage> for ServerMessage {
fn from(msg: ClientMgrMessage) -> Self {
use ClientMgrMessage::BroadcastGlobalMessage;
match msg {
BroadcastGlobalMessage { from, content } => {
ServerMessage::BroadcastGlobalMessage { from, content }
}
_ => unimplemented!(),
}
}
}
// impl From<PluginManagerMessage> for ServerMessage {
// fn from(_: PluginManagerMessage) -> Self {
// todo!()
// }
// }
/// # Server
/// authors: @michael-bailey, @Mitch161
/// This Represents a server instance.
/// It is composed of a client manager and a network manager.
///
/// # Attributes
/// - client_manager: The servers client manager.
/// - network_manager: The servers network manager.
/// - receiver: The servers channel for communication by managers.
/// - lua: The servers lua context, used for running lua scripts.
///
pub struct Server {
pub client_manager: Arc<ClientManager<ServerMessage>>,
network_manager: Arc<NetworkManager<ServerMessage>>,
// plugin_manager: Arc<PluginManager<ServerMessage>>,
receiver: Mutex<Receiver<ServerMessage>>,
}
impl Server {
/// Create a new server object
pub async fn new() -> Result<Arc<Server>, Error> {
let (sender, receiver) = channel(1024);
let server = Arc::new(Server {
client_manager: ClientManager::new(sender.clone()),
network_manager: NetworkManager::new("0.0.0.0:5600", sender.clone()).await?,
// plugin_manager: PluginManager::new(sender),
receiver: Mutex::new(receiver),
});
Ok(server)
}
pub async fn port(self: &Arc<Server>) -> u16 {
self.network_manager.port().await
}
pub async fn start(self: &Arc<Server>) {
// start client manager and network manager
self.network_manager.clone().start();
self.client_manager.clone().start();
// let _ = self.plugin_manager.clone().load().await;
// clone block items
let server = self.clone();
loop {
let mut lock = server.receiver.lock().await;
if let Some(message) = lock.recv().await {
println!("[server]: received message {:?}", &message);
match message {
ServerMessage::ClientConnected {
uuid,
address,
username,
connection,
} => {
server
.client_manager
.add_client(uuid, username, address, connection)
.await
}
ServerMessage::BroadcastGlobalMessage {
from: _,
content: _,
} => {
// server
// .client_manager
// .clone()
// .send_message(
// ClientMgrMessage::BroadcastGlobalMessage {sender, content}
// ).await
}
#[allow(unreachable_patterns)]
_ => {
unimplemented!()
}
}
}
}
}
}