Inital commit

This commit is contained in:
Leon Grünewald 2024-04-30 01:00:59 +02:00
commit 88aacacb19
13 changed files with 3908 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/target
.idea
server-config.json
client-config.json

3617
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

29
Cargo.toml Normal file
View 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"

View file

View file

@ -0,0 +1,4 @@
{
"listen": "127.0.0.1",
"port": 8000
}

85
src/client/main.rs Normal file
View 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
View 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
View file

@ -0,0 +1,2 @@
pub mod packets;
pub mod user;

10
src/common/packets.rs Normal file
View 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
View file

@ -0,0 +1,4 @@
struct User {
id: u32,
username: String
}

7
src/server/config.rs Normal file
View 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
View 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
View 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}")
}
}
}