1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
#![feature(async_fn_in_trait)]
#![feature(custom_inner_attributes)]
#![feature(lint_reasons)]
#![feature(never_type)]
#![feature(lazy_cell)]
#![clippy::msrv = "1.69.0"]
#![deny(clippy::nursery)]
#![deny(clippy::pedantic)]
#![deny(clippy::alloc_instead_of_core)]
#![deny(clippy::as_underscore)]
#![deny(clippy::clone_on_ref_ptr)]
#![deny(clippy::create_dir)]
#![warn(clippy::dbg_macro)]
#![deny(clippy::default_numeric_fallback)]
#![deny(clippy::default_union_representation)]
#![deny(clippy::deref_by_slicing)]
#![deny(clippy::else_if_without_else)]
#![deny(clippy::empty_structs_with_brackets)]
#![deny(clippy::exit)]
#![deny(clippy::expect_used)]
#![deny(clippy::filetype_is_file)]
#![deny(clippy::fn_to_numeric_cast)]
#![deny(clippy::format_push_string)]
#![deny(clippy::get_unwrap)]
#![deny(clippy::if_then_some_else_none)]
#![allow(
clippy::implicit_return,
reason = "returns should be done implicitly, not explicitly"
)]
#![deny(clippy::indexing_slicing)]
#![deny(clippy::large_include_file)]
#![deny(clippy::let_underscore_must_use)]
#![deny(clippy::lossy_float_literal)]
#![deny(clippy::map_err_ignore)]
#![deny(clippy::mem_forget)]
#![deny(clippy::missing_docs_in_private_items)]
#![deny(clippy::missing_trait_methods)]
#![deny(clippy::multiple_inherent_impl)]
#![deny(clippy::needless_return)]
#![deny(clippy::non_ascii_literal)]
#![deny(clippy::panic_in_result_fn)]
#![deny(clippy::pattern_type_mismatch)]
#![deny(clippy::rc_buffer)]
#![deny(clippy::rc_mutex)]
#![deny(clippy::rest_pat_in_fully_bound_structs)]
#![deny(clippy::same_name_method)]
#![deny(clippy::separated_literal_suffix)]
#![deny(clippy::str_to_string)]
#![deny(clippy::string_add)]
#![deny(clippy::string_slice)]
#![deny(clippy::string_to_string)]
#![allow(
clippy::tabs_in_doc_comments,
reason = "tabs are preferred for this project"
)]
#![deny(clippy::try_err)]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(clippy::unnecessary_self_imports)]
#![deny(clippy::unneeded_field_pattern)]
#![deny(clippy::unwrap_in_result)]
#![deny(clippy::unwrap_used)]
#![warn(clippy::use_debug)]
#![deny(clippy::verbose_file_reads)]
#![deny(clippy::wildcard_dependencies)]
#![deny(clippy::wildcard_enum_match_arm)]
#![deny(clippy::missing_panics_doc)]
#![deny(missing_copy_implementations)]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![deny(single_use_lifetimes)]
#![deny(unsafe_code)]
#![deny(unused)]
// Server-specific lint disables
#![allow(clippy::redundant_pub_crate)]
//! # Messenger Server
//!
//! Provides a server-side implementation of the messenger protocol
mod app;
mod message;
mod session;
mod websocket;
use std::{
collections::{HashSet, VecDeque},
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
sync::Arc,
};
use app::State as AppState;
use axum::{response::Html, routing::get, Router};
use tokio::sync::{broadcast, Mutex};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
/// Socket Address the server is bound to when ran.
const ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 3000));
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "chat=trace".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
// Set up application state for use with with_state().
let user_set: Mutex<HashSet<String>> = Mutex::new(HashSet::new());
let (tx, _rx) = broadcast::channel::<String>(100);
let message_history = Mutex::new(VecDeque::new());
let app_state = Arc::new(AppState {
message_history,
tx,
user_set,
});
let app = Router::new()
.route("/", get(index))
.route("/websocket", get(websocket::handler))
.with_state(app_state);
tracing::debug!("listening on {ADDR}");
if let Err(error) = axum::Server::bind(&ADDR)
.serve(app.into_make_service())
.await
{
tracing::error!("server failed to start: {error}");
}
}
/// Include utf-8 file at **compile** time.
#[expect(
clippy::unused_async,
reason = "axum requires this function to by async, but clippy disallows this"
)]
async fn index() -> Html<&'static str> {
Html(std::include_str!("../chat.html"))
}
|