Inital commit
This commit is contained in:
commit
88aacacb19
13 changed files with 3908 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
.idea
|
||||
server-config.json
|
||||
client-config.json
|
3617
Cargo.lock
generated
Normal file
3617
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
29
Cargo.toml
Normal file
29
Cargo.toml
Normal file
|
@ -0,0 +1,29 @@
|
|||
[package]
|
||||
name = "fedichat"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
figment = { version = "0.10", features = ["json", "env"] }
|
||||
futures-util = "0.3.30"
|
||||
serde = { version = "1.0", features = ["derive", "alloc", "std"] }
|
||||
serde_json = { version = "1.0" , features = ["default"]}
|
||||
tokio = { version = "1.37.0", features = ["full", "rt-multi-thread"]}
|
||||
tokio-tungstenite = { version = "0.21.0", features = ["default", "rustls"]}
|
||||
iced = { version = "0.12", features = ["default"]}
|
||||
iced_futures = { version = "0.12", features = ["tokio"] }
|
||||
|
||||
[[bin]]
|
||||
name = "client"
|
||||
path = "src/client/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "server"
|
||||
path = "src/server/main.rs"
|
||||
|
||||
[lib]
|
||||
name = "common"
|
||||
path = "src/common/lib.rs"
|
0
client-config.json.example
Normal file
0
client-config.json.example
Normal file
4
server-config.json.example
Normal file
4
server-config.json.example
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 8000
|
||||
}
|
85
src/client/main.rs
Normal file
85
src/client/main.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
mod websocket;
|
||||
|
||||
use iced::widget::{column, Column, button, text_input, text};
|
||||
use iced::{Application, Command, Element, Pixels, settings::Settings};
|
||||
use iced_futures::Subscription;
|
||||
use websocket::websocket;
|
||||
use crate::websocket::AppWebsocketConfig;
|
||||
|
||||
fn main() -> iced::Result {
|
||||
let settings = Settings {
|
||||
id: None,
|
||||
window: Default::default(),
|
||||
flags: (),
|
||||
fonts: vec![],
|
||||
default_font: Default::default(),
|
||||
default_text_size: Pixels(16.0),
|
||||
antialiasing: true,
|
||||
};
|
||||
|
||||
ChatApp::run(settings)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ChatApp {
|
||||
host: String
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum ChatAppEvent {
|
||||
ConnectButtonPress,
|
||||
HostTextInputChanged(String)
|
||||
}
|
||||
|
||||
impl ChatApp {
|
||||
fn view_auth(&self) -> Element<<ChatApp as Application>::Message> {
|
||||
column![
|
||||
text_input("Host", &self.host)
|
||||
.on_input(ChatAppEvent::HostTextInputChanged)
|
||||
.on_submit(ChatAppEvent::ConnectButtonPress),
|
||||
button("Connect")
|
||||
.on_press(ChatAppEvent::ConnectButtonPress)
|
||||
].into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Application for ChatApp {
|
||||
type Executor = iced_futures::backend::native::tokio::Executor;
|
||||
type Message = ChatAppEvent;
|
||||
type Theme = iced::theme::Theme;
|
||||
type Flags = ();
|
||||
|
||||
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
(Self {
|
||||
host: String::from("127.0.0.1:3000"),
|
||||
}, Command::none())
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Chat app")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: ChatAppEvent) -> Command<Self::Message> {
|
||||
println!("update");
|
||||
match message {
|
||||
ChatAppEvent::ConnectButtonPress => {
|
||||
println!("ConnectButtonPress")
|
||||
},
|
||||
ChatAppEvent::HostTextInputChanged(host_value) => {
|
||||
self.host = host_value;
|
||||
}
|
||||
}
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Self::Message> {
|
||||
println!("view");
|
||||
self.view_auth()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Self::Message> {
|
||||
println!("subscription");
|
||||
websocket(AppWebsocketConfig::new(self.host.clone(), None, None))
|
||||
}
|
||||
}
|
40
src/client/websocket.rs
Normal file
40
src/client/websocket.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use iced::subscription::{Subscription, channel};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum AppWebsocketState {
|
||||
Disconnected,
|
||||
Connected
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct AppWebsocketConfig {
|
||||
host: String,
|
||||
user: Option<String>,
|
||||
pass: Option<String>
|
||||
}
|
||||
|
||||
impl AppWebsocketConfig {
|
||||
pub fn new(host: String, user: Option<String>, pass: Option<String>) -> Self {
|
||||
Self {
|
||||
host,
|
||||
user,
|
||||
pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn websocket(config: AppWebsocketConfig) -> Subscription<super::ChatAppEvent> {
|
||||
struct Websocket;
|
||||
|
||||
channel(
|
||||
std::any::TypeId::of::<Websocket>(),
|
||||
100,
|
||||
|mut output| async move {
|
||||
let mut state = AppWebsocketState::Disconnected;
|
||||
|
||||
loop {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
2
src/common/lib.rs
Normal file
2
src/common/lib.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod packets;
|
||||
pub mod user;
|
10
src/common/packets.rs
Normal file
10
src/common/packets.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Message {
|
||||
Text { chat: (), content: String }
|
||||
}
|
||||
|
||||
// pub struct GroupPermissions {}
|
||||
// pub struct UserPermissions {}
|
||||
// pub struct RoomPermissions {}
|
4
src/common/user.rs
Normal file
4
src/common/user.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
struct User {
|
||||
id: u32,
|
||||
username: String
|
||||
}
|
7
src/server/config.rs
Normal file
7
src/server/config.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Config {
|
||||
pub host: String,
|
||||
pub port: u16
|
||||
}
|
13
src/server/db.rs
Normal file
13
src/server/db.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
trait DatabaseProvider {
|
||||
fn message_create();
|
||||
fn message_edit();
|
||||
fn message_delete();
|
||||
|
||||
fn chat_create();
|
||||
fn chat_edit();
|
||||
fn chat_delete();
|
||||
|
||||
fn user_create();
|
||||
fn user_edit();
|
||||
fn user_delete();
|
||||
}
|
93
src/server/main.rs
Normal file
93
src/server/main.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
mod config;
|
||||
mod db;
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use tokio::{net::{TcpListener, TcpStream}, sync::Mutex};
|
||||
use crate::config::Config;
|
||||
use figment::{Figment, providers::{Format, Env, Json}};
|
||||
use futures_util::{StreamExt, stream::SplitSink, TryStreamExt, };
|
||||
use tokio_tungstenite::{WebSocketStream, tungstenite::Message as TungsteniteMessage};
|
||||
use common::packets::Message;
|
||||
|
||||
type PeerList = Arc<Mutex<Vec<Peer>>>;
|
||||
|
||||
pub struct Peer {
|
||||
pub addr: SocketAddr,
|
||||
pub websocket_tx: SplitSink<WebSocketStream<TcpStream>, TungsteniteMessage>
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
fn new(addr: SocketAddr, websocket_tx: SplitSink<WebSocketStream<TcpStream>, TungsteniteMessage>) -> Self {
|
||||
Self {
|
||||
addr,
|
||||
websocket_tx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let config: Config = Figment::new()
|
||||
.merge(Json::file("server-config.json"))
|
||||
.merge(Env::prefixed("APP_"))
|
||||
.extract()?;
|
||||
|
||||
let peer_list = Arc::new(Mutex::new(Vec::new()));
|
||||
let tcp_listener = TcpListener::bind(format!("{}:{}", config.host, config.port)).await?;
|
||||
println!("Listening on {}:{}", config.host, config.port);
|
||||
|
||||
while let Ok((stream, addr)) = tcp_listener.accept().await {
|
||||
println!("Connection from {}", &addr);
|
||||
tokio::spawn(handle_connection(stream, addr, peer_list.clone()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_connection(stream: TcpStream, addr: SocketAddr, peer_list: PeerList) {
|
||||
let websocket_stream = tokio_tungstenite::accept_async(stream)
|
||||
.await
|
||||
.expect("Could not initialize websocket stream");
|
||||
|
||||
let (mut websocket_tx, mut websocket_rx) = websocket_stream.split();
|
||||
|
||||
{
|
||||
let mut locked_peer_list = peer_list.lock().await;
|
||||
locked_peer_list.push(Peer::new(addr, websocket_tx));
|
||||
}
|
||||
|
||||
while let Some(socket_result) = websocket_rx.next().await {
|
||||
match socket_result {
|
||||
Ok(websocket_message) => {
|
||||
match websocket_message {
|
||||
TungsteniteMessage::Text(text_message) => {
|
||||
match serde_json::from_str(&text_message) {
|
||||
Ok(message) => {
|
||||
handle_message(message, addr, peer_list.clone())
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("ParseError: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Websocket: Unsupported message type");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("SocketError: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_message(message: Message, sender: SocketAddr, peer_list: PeerList) {
|
||||
match message {
|
||||
Message::Text { chat, content} => {
|
||||
println!("{chat}: {content}")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue