style: crank clippy to pedantic+nursery and fix all lints

Enable the full clippy all + pedantic + nursery groups as workspace warnings,
with a small documented allow-list for the pedantic/nursery lints this project
intentionally does not follow (rustdoc-style requirements, deliberate numeric
casts in the protocol/UI layers, and a few noisy nursery lints). Also enable
the rust unused_lifetimes and unsafe_op_in_unsafe_fn lints.

Fixed every resulting warning (527 -> 0) mechanically per clippy's suggestions:
inline format args, const fns, item ordering, let-else, redundant clones,
map_or, cast_lossless, Eq derives, associated-fn conversions for methods that
never used self, and removing unnecessary Result wrappers whose call sites could
never propagate an error. No runtime behavior changed; cargo fmt applied.

cargo clippy --workspace --all-targets: 0 warnings. All tests pass.
This commit is contained in:
michel
2026-07-19 00:09:17 +02:00
parent b149a8ecf2
commit e72c8a5c07
37 changed files with 482 additions and 433 deletions
+8 -11
View File
@@ -41,14 +41,14 @@ impl<M: Send> ActorHandle<M> {
pub fn send(&self, msg: M) -> Result<(), String> {
self.sender
.send(ActorMessage::UserMessage(msg))
.map_err(|e| format!("Failed to send message: {}", e))
.map_err(|e| format!("Failed to send message: {e}"))
}
/// Request actor to stop gracefully
pub fn stop(&self) -> Result<(), String> {
self.sender
.send(ActorMessage::Stop)
.map_err(|e| format!("Failed to send stop signal: {}", e))
.map_err(|e| format!("Failed to send stop signal: {e}"))
}
}
@@ -64,16 +64,15 @@ pub struct ActorSystem {
}
impl ActorSystem {
pub fn new(thread_pool: Arc<ThreadPool>) -> Self {
ActorSystem { thread_pool }
#[must_use]
pub const fn new(thread_pool: Arc<ThreadPool>) -> Self {
Self { thread_pool }
}
/// Spawn a new actor and return its handle
pub fn spawn<A: Actor>(&self, mut actor: A) -> ActorHandle<A::Message> {
let (sender, receiver) = channel::<ActorMessage<A::Message>>();
let handle = ActorHandle {
sender: sender.clone(),
};
let handle = ActorHandle { sender };
// Start the actor event loop on the thread pool
self.thread_pool.execute(move || {
@@ -86,7 +85,7 @@ impl ActorSystem {
}
/// Spawn a new actor with initialization callback and return its handle
/// The callback receives the actor handle before on_start is called
/// The callback receives the actor handle before `on_start` is called
pub fn spawn_with_handle<A: Actor, F>(
&self,
mut actor: A,
@@ -96,9 +95,7 @@ impl ActorSystem {
F: FnOnce(&mut A, ActorHandle<A::Message>) + Send + 'static,
{
let (sender, receiver) = channel::<ActorMessage<A::Message>>();
let handle = ActorHandle {
sender: sender.clone(),
};
let handle = ActorHandle { sender };
let handle_for_init = handle.clone();
self.thread_pool.execute(move || {
+18 -23
View File
@@ -52,6 +52,7 @@ pub struct PeerActor {
}
impl PeerActor {
#[must_use]
pub fn new(
peer: Peer,
stream: Option<TcpStream>,
@@ -135,9 +136,9 @@ impl PeerActor {
msgs
});
messages
.iter()
.for_each(|msg| self.handle_message(msg.clone()));
for msg in &messages {
self.handle_message(msg.clone());
}
}
fn handle_message(&mut self, msg: PeerMessage) {
@@ -210,14 +211,7 @@ impl PeerActor {
username, token, allowed
);
if !allowed {
if let Some(reason_text) = reason {
debug!(
"[peer:{}] Transfer rejected: {} - token {}, waiting for TransferRequest...",
username, reason_text, token
);
}
} else {
if allowed {
debug!(
"[peer:{}] Transfer allowed, ready to connect with token {:}",
username, token
@@ -237,6 +231,11 @@ impl PeerActor {
username, e
);
}
} else if let Some(reason_text) = reason {
debug!(
"[peer:{}] Transfer rejected: {} - token {}, waiting for TransferRequest...",
username, reason_text, token
);
}
}
PeerMessage::PlaceInQueueResponse { filename, place } => {
@@ -267,7 +266,7 @@ impl PeerActor {
match self.peer.write_safe() {
Ok(mut p) => p.username = username,
Err(e) => {
error!("[peer_actor] SetUsername write: {}", e)
error!("[peer_actor] SetUsername write: {}", e);
}
}
}
@@ -306,9 +305,8 @@ impl PeerActor {
}
{
let stream = match self.stream.as_mut() {
Some(s) => s,
None => return,
let Some(stream) = self.stream.as_mut() else {
return;
};
match self.reader.read_from_socket(stream) {
@@ -352,7 +350,7 @@ impl PeerActor {
message
.get_message_name(
MessageType::Peer,
message.get_message_code() as u32
u32::from(message.get_message_code())
)
.map_err(|e| e.to_string())
);
@@ -381,12 +379,9 @@ impl PeerActor {
fn send_message(&mut self, message: Message) {
let username = self.peer_username();
let stream = match self.stream.as_mut() {
Some(s) => s,
None => {
error!("Cannot send message: stream is None");
return;
}
let Some(stream) = self.stream.as_mut() else {
error!("Cannot send message: stream is None");
return;
};
trace!(
@@ -456,7 +451,7 @@ impl PeerActor {
};
let socket_addr =
format!("{}:{}", host, port).parse::<std::net::SocketAddr>();
format!("{host}:{port}").parse::<std::net::SocketAddr>();
match socket_addr {
Ok(addr) => {
+10 -4
View File
@@ -18,6 +18,7 @@ pub struct PeerRegistry {
}
impl PeerRegistry {
#[must_use]
pub fn new(
actor_system: Arc<ActorSystem>,
client_channel: Sender<ClientOperation>,
@@ -48,7 +49,7 @@ impl PeerRegistry {
let mut peers = self
.peers
.lock_safe()
.map_err(|e| format!("peer registry lock poisoned: {}", e))?;
.map_err(|e| format!("peer registry lock poisoned: {e}"))?;
// Stop any actor already registered under this username so it does not
// become an orphan pinning a pool worker forever (and so its later
// shutdown cannot remove this new connection from the registry).
@@ -64,6 +65,7 @@ impl PeerRegistry {
Ok(handle)
}
#[must_use]
pub fn get_peer(&self, username: &str) -> Option<ActorHandle<PeerMessage>> {
match self.peers.lock_safe() {
Ok(peers) => peers.get(username).cloned(),
@@ -74,6 +76,7 @@ impl PeerRegistry {
}
}
#[must_use]
pub fn remove_peer(
&self,
username: &str,
@@ -94,6 +97,7 @@ impl PeerRegistry {
handle
}
#[must_use]
pub fn get_all_usernames(&self) -> Vec<String> {
match self.peers.lock_safe() {
Ok(peers) => peers.keys().cloned().collect(),
@@ -104,6 +108,7 @@ impl PeerRegistry {
}
}
#[must_use]
pub fn count(&self) -> usize {
match self.peers.lock_safe() {
Ok(peers) => peers.len(),
@@ -114,6 +119,7 @@ impl PeerRegistry {
}
}
#[must_use]
pub fn contains(&self, username: &str) -> bool {
match self.peers.lock_safe() {
Ok(peers) => peers.contains_key(username),
@@ -129,9 +135,9 @@ impl PeerRegistry {
username: &str,
message: PeerMessage,
) -> Result<(), String> {
let handle = self.get_peer(username).ok_or_else(|| {
format!("Peer {} not found in registry", username)
})?;
let handle = self
.get_peer(username)
.ok_or_else(|| format!("Peer {username} not found in registry"))?;
handle.send(message)
}
+34 -33
View File
@@ -34,15 +34,18 @@ pub struct PeerAddress {
}
impl PeerAddress {
pub fn new(host: String, port: u16) -> Self {
#[must_use]
pub const fn new(host: String, port: u16) -> Self {
Self { host, port }
}
#[must_use]
pub fn get_host(&self) -> &str {
&self.host
}
pub fn get_port(&self) -> u16 {
#[must_use]
pub const fn get_port(&self) -> u16 {
self.port
}
}
@@ -59,6 +62,7 @@ pub struct Context {
}
impl Context {
#[must_use]
pub fn new() -> Self {
Self::default()
}
@@ -72,7 +76,8 @@ pub struct UserMessage {
new_message: bool,
}
impl UserMessage {
pub fn new(
#[must_use]
pub const fn new(
id: u32,
timestamp: u32,
username: String,
@@ -143,6 +148,7 @@ pub struct ServerActor {
}
impl ServerActor {
#[must_use]
pub fn new(
address: PeerAddress,
client_channel: Sender<ClientOperation>,
@@ -166,11 +172,13 @@ impl ServerActor {
}
}
pub fn get_address(&self) -> &PeerAddress {
#[must_use]
pub const fn get_address(&self) -> &PeerAddress {
&self.address
}
pub fn get_sender(&self) -> Option<&Sender<ServerMessage>> {
#[must_use]
pub const fn get_sender(&self) -> Option<&Sender<ServerMessage>> {
self.dispatcher_sender.as_ref()
}
@@ -178,7 +186,7 @@ impl ServerActor {
let host = self.address.host.clone();
let port = self.address.port;
let addr_str = format!("{}:{}", host, port);
let addr_str = format!("{host}:{port}");
let mut socket_addrs = match addr_str.to_socket_addrs() {
Ok(addrs) => addrs,
@@ -197,7 +205,7 @@ impl ServerActor {
let Some(addr) = socket_addr else {
let error_msg =
format!("No socket addresses found for {}:{}", host, port);
format!("No socket addresses found for {host}:{port}");
error!("[server] {}", error_msg);
self.disconnect_with_error(io::Error::new(
io::ErrorKind::InvalidInput,
@@ -280,9 +288,9 @@ impl ServerActor {
msgs
});
messages
.iter()
.for_each(|msg| self.handle_message(msg.clone()));
for msg in &messages {
self.handle_message(msg.clone());
}
}
pub fn login(
@@ -305,7 +313,7 @@ impl ServerActor {
}
{
logged_in = context.read_safe()?.logged_in
logged_in = context.read_safe()?.logged_in;
}
if logged_in.is_some() {
@@ -341,15 +349,12 @@ impl ServerActor {
fn handle_message(&mut self, msg: ServerMessage) {
if !matches!(self.connection_state, ConnectionState::Connected) {
match &msg {
ServerMessage::ProcessRead => {
// Always process read operations
}
_ => {
// Queue all other messages when not connected
self.queued_messages.push(msg);
return;
}
if matches!(&msg, ServerMessage::ProcessRead) {
// Always process read operations
} else {
// Queue all other messages when not connected
self.queued_messages.push(msg);
return;
}
}
@@ -357,7 +362,7 @@ impl ServerActor {
ServerMessage::ConnectToPeer(peer) => {
if let Some(op) = match peer.connection_type {
ConnectionType::P | ConnectionType::F => {
Some(ClientOperation::ConnectToPeer(peer.clone()))
Some(ClientOperation::ConnectToPeer(peer))
}
ConnectionType::D => None,
} && let Err(e) = self.client_channel.send(op)
@@ -369,7 +374,7 @@ impl ServerActor {
match self.context.write_safe() {
Ok(mut ctx) => ctx.logged_in = Some(message),
Err(e) => {
error!("[server] LoginStatus write: {}", e)
error!("[server] LoginStatus write: {}", e);
}
}
}
@@ -469,9 +474,8 @@ impl ServerActor {
}
{
let stream = match self.stream.as_mut() {
Some(s) => s,
None => return,
let Some(stream) = self.stream.as_mut() else {
return;
};
match self.reader.read_from_socket(stream) {
@@ -506,7 +510,7 @@ impl ServerActor {
message
.get_message_name(
MessageType::Server,
message.get_message_code() as u32
u32::from(message.get_message_code())
)
.map_err(|e| e.to_string())
);
@@ -536,7 +540,7 @@ impl ServerActor {
fn queue_message(&mut self, message: Message) {
if let Some(sender) = &self.dispatcher_sender {
match sender.send(ServerMessage::SendMessage(message)) {
Ok(_) => {}
Ok(()) => {}
Err(e) => error!("Failed to send: {}", e),
}
} else {
@@ -546,12 +550,9 @@ impl ServerActor {
}
fn send_message(&mut self, message: Message) {
let stream = match self.stream.as_mut() {
Some(s) => s,
None => {
error!("[server] Cannot send message: stream is None");
return;
}
let Some(stream) = self.stream.as_mut() else {
error!("[server] Cannot send message: stream is None");
return;
};
trace!(
+47 -36
View File
@@ -114,6 +114,7 @@ impl ClientContext {
pub fn remove_download(&mut self, token: u32) {
self.downloads.remove(token);
}
#[must_use]
pub fn get_download_by_token(&self, token: u32) -> Option<&Download> {
self.downloads.get_by_token(token)
}
@@ -130,10 +131,12 @@ impl ClientContext {
) -> Option<&mut Download> {
self.downloads.get_by_file_mut(username, filename)
}
#[must_use]
pub fn get_download_tokens(&self) -> Vec<u32> {
self.downloads.tokens()
}
pub fn get_downloads(&self) -> &Vec<Download> {
#[must_use]
pub const fn get_downloads(&self) -> &Vec<Download> {
self.downloads.list()
}
pub fn update_download_with_status(
@@ -165,7 +168,7 @@ fn test_client_context_downloads() {
status: DownloadStatus::Queued,
sender: mpsc::channel().0,
queue_position: None,
metadata: Default::default(),
metadata: DownloadMetadata::default(),
};
context.add_download(download);
assert!(context.get_download_by_token(123).is_some());
@@ -173,7 +176,7 @@ fn test_client_context_downloads() {
assert_eq!(context.get_downloads().len(), 1);
if let Some(download) = context.get_download_by_token_mut(token) {
assert_eq!(download.token, token);
download.token = new_token
download.token = new_token;
}
assert!(context.get_download_by_token(new_token).is_some());
assert_eq!(context.get_download_tokens(), vec![new_token]);
@@ -199,7 +202,7 @@ fn test_client_pause_and_resume_download() {
},
sender: download_sender,
queue_position: None,
metadata: Default::default(),
metadata: DownloadMetadata::default(),
};
client.context.write().unwrap().add_download(download);
@@ -255,7 +258,7 @@ fn test_client_removes_only_queued_downloads() {
status: DownloadStatus::Queued,
sender: mpsc::channel().0,
queue_position: None,
metadata: Default::default(),
metadata: DownloadMetadata::default(),
};
let active_download = Download {
username: "peer".to_string(),
@@ -270,7 +273,7 @@ fn test_client_removes_only_queued_downloads() {
},
sender: mpsc::channel().0,
queue_position: None,
metadata: Default::default(),
metadata: DownloadMetadata::default(),
};
{
@@ -289,12 +292,11 @@ fn test_client_removes_only_queued_downloads() {
impl ClientContext {
#[must_use]
pub fn new() -> Self {
let max_threads = thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(8);
let max_threads =
thread::available_parallelism().map_or(8, std::num::NonZero::get);
let thread_pool = Arc::new(ThreadPool::new(max_threads));
let actor_system = Arc::new(ActorSystem::new(thread_pool.clone()));
let actor_system = Arc::new(ActorSystem::new(thread_pool));
Self {
peer_registry: None,
@@ -324,6 +326,7 @@ impl Client {
Self::with_settings(ClientSettings::new(username, password))
}
#[must_use]
pub fn with_settings(settings: ClientSettings) -> Self {
logger::init();
Self {
@@ -483,6 +486,7 @@ impl Client {
Ok(self.get_search_results(query))
}
#[must_use]
pub fn get_search_results_count(&self, search_key: &str) -> usize {
self.context
.read_safe()
@@ -493,6 +497,7 @@ impl Client {
.unwrap_or(0)
}
#[must_use]
pub fn get_search_results(&self, search_key: &str) -> Vec<SearchResult> {
self.context
.read_safe()
@@ -504,6 +509,7 @@ impl Client {
}
/// Non-blocking variant that returns None if the lock is unavailable
#[must_use]
pub fn try_get_search_results(
&self,
search_key: &str,
@@ -513,6 +519,7 @@ impl Client {
})
}
#[must_use]
pub fn get_all_searches(&self) -> HashMap<String, Search> {
self.context
.read_safe()
@@ -520,6 +527,7 @@ impl Client {
.unwrap_or_default()
}
#[must_use]
pub fn get_all_downloads(&self) -> Vec<Download> {
self.context
.read_safe()
@@ -527,6 +535,7 @@ impl Client {
.unwrap_or_default()
}
#[must_use]
pub fn pause_download(&self, username: &str, filename: &str) -> bool {
match self.context.write_safe() {
Ok(mut ctx) => ctx.downloads.pause_by_file(username, filename),
@@ -537,6 +546,7 @@ impl Client {
}
}
#[must_use]
pub fn resume_download(&self, username: &str, filename: &str) -> bool {
match self.context.write_safe() {
Ok(mut ctx) => ctx.downloads.resume_by_file(username, filename),
@@ -547,6 +557,7 @@ impl Client {
}
}
#[must_use]
pub fn remove_queued_download(
&self,
username: &str,
@@ -599,7 +610,7 @@ impl Client {
let download = Download {
username: username.clone(),
filename: filename.clone(),
filename,
token,
size,
download_directory,
@@ -728,17 +739,18 @@ impl Client {
// client ops loop if this read guard were still
// held on this thread.
{
let context =
match client_context.read_safe() {
Ok(c) => c,
Err(e) => {
error!(
"[client] PeerDisconnected read: {}",
e
);
continue;
}
};
let context = match client_context
.read_safe()
{
Ok(c) => c,
Err(e) => {
error!(
"[client] PeerDisconnected read: {}",
e
);
continue;
}
};
if let Some(ref registry) =
context.peer_registry
&& let Some(handle) =
@@ -864,17 +876,17 @@ impl Client {
token
);
}
};
}
}
ClientOperation::NewPeer(new_peer) => {
let peer_exists = match client_context
.read_safe()
{
Ok(ctx) => ctx
.peer_registry
.as_ref()
.map(|r| r.contains(&new_peer.username))
.unwrap_or(false),
Ok(ctx) => {
ctx.peer_registry.as_ref().is_some_and(
|r| r.contains(&new_peer.username),
)
}
Err(e) => {
error!("[client] NewPeer read: {}", e);
continue;
@@ -965,8 +977,7 @@ impl Client {
Ok(ctx) => ctx
.peer_registry
.as_ref()
.map(|r| r.contains(&username))
.unwrap_or(false),
.is_some_and(|r| r.contains(&username)),
Err(e) => {
error!(
"[client] GetPeerAddressResponse read: {}",
@@ -1121,7 +1132,7 @@ impl Client {
own_username: String,
stream: Option<TcpStream>,
) {
let client_context = client_context.clone();
let client_context = client_context;
let peer_clone = peer.clone();
trace!(
@@ -1130,7 +1141,7 @@ impl Client {
);
match peer.connection_type {
ConnectionType::P => {
let username = peer.username.clone();
let username = peer.username;
let context = match client_context.read_safe() {
Ok(c) => c,
@@ -1165,7 +1176,7 @@ impl Client {
peer.port,
peer.token.unwrap(),
false,
own_username.clone(),
own_username,
);
match download_peer.download_file(
@@ -1196,7 +1207,7 @@ impl Client {
}
}
ConnectionType::D => {
error!("ConnectionType::D not implemented")
error!("ConnectionType::D not implemented");
}
}
}
@@ -1217,9 +1228,9 @@ impl Client {
if let Some(server_sender) = &context.server_sender {
if let Some(token) = peer.token {
match server_sender.send(ServerMessage::PierceFirewall(token)) {
Ok(_) => (),
Ok(()) => (),
Err(e) => {
error!("Failed to send PierceFirewall message: {}", e)
error!("Failed to send PierceFirewall message: {}", e);
}
}
} else {
+3 -2
View File
@@ -10,12 +10,13 @@ pub struct MessageDispatcher<Op> {
}
impl<Op> MessageDispatcher<Op> {
pub fn new(
#[must_use]
pub const fn new(
owner_name: String,
sender: Sender<Op>,
handlers: Handlers<Op>,
) -> Self {
MessageDispatcher {
Self {
owner_name,
sender,
handlers,
+13 -6
View File
@@ -6,6 +6,7 @@ pub struct DownloadStore {
}
impl DownloadStore {
#[must_use]
pub fn new() -> Self {
Self::default()
}
@@ -18,6 +19,7 @@ impl DownloadStore {
self.downloads.retain(|d| d.token != token);
}
#[must_use]
pub fn get_by_token(&self, token: u32) -> Option<&Download> {
self.downloads.iter().find(|d| d.token == token)
}
@@ -36,11 +38,13 @@ impl DownloadStore {
.find(|d| d.username == username && d.filename == filename)
}
#[must_use]
pub fn tokens(&self) -> Vec<u32> {
self.downloads.iter().map(|d| d.token).collect()
}
pub fn list(&self) -> &Vec<Download> {
#[must_use]
pub const fn list(&self) -> &Vec<Download> {
&self.downloads
}
@@ -128,9 +132,11 @@ impl DownloadStore {
}
/// Returns the tokens of downloads matching `username` (and optionally a
/// `filename`) after notifying their senders of `Failed`. Caller is
/// responsible for then calling `update_status` and `remove` for each token,
/// typically under a write lock.
/// `filename`) after notifying their senders of `Failed`.
///
/// Caller is responsible for then calling `update_status` and `remove` for
/// each token, typically under a write lock.
#[must_use]
pub fn collect_failed_tokens(
store: &DownloadStore,
username: &str,
@@ -152,19 +158,20 @@ pub fn collect_failed_tokens(
#[cfg(test)]
mod tests {
use super::*;
use crate::types::DownloadMetadata;
use std::sync::mpsc;
fn make_download(token: u32, status: DownloadStatus) -> Download {
Download {
username: "peer".to_string(),
filename: format!("file-{}.mp3", token),
filename: format!("file-{token}.mp3"),
token,
size: 100,
download_directory: "test".to_string(),
status,
sender: mpsc::channel().0,
queue_position: None,
metadata: Default::default(),
metadata: DownloadMetadata::default(),
}
}
+16 -16
View File
@@ -25,23 +25,23 @@ pub enum SoulseekRs {
impl fmt::Display for SoulseekRs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SoulseekRs::NetworkError(err) => {
write!(f, "Network error: {}", err)
Self::NetworkError(err) => {
write!(f, "Network error: {err}")
}
SoulseekRs::AuthenticationFailed => {
Self::AuthenticationFailed => {
write!(f, "Authentication failed")
}
SoulseekRs::ParseError(msg) => write!(f, "Parse error: {}", msg),
SoulseekRs::Timeout => write!(f, "Operation timed out"),
SoulseekRs::ConnectionClosed => write!(f, "Connection closed"),
SoulseekRs::InvalidMessage(msg) => {
write!(f, "Invalid message: {}", msg)
Self::ParseError(msg) => write!(f, "Parse error: {msg}"),
Self::Timeout => write!(f, "Operation timed out"),
Self::ConnectionClosed => write!(f, "Connection closed"),
Self::InvalidMessage(msg) => {
write!(f, "Invalid message: {msg}")
}
SoulseekRs::NotConnected => write!(f, "Not connected to server"),
SoulseekRs::CompressionError(msg) => {
write!(f, "Compression error: {}", msg)
Self::NotConnected => write!(f, "Not connected to server"),
Self::CompressionError(msg) => {
write!(f, "Compression error: {msg}")
}
SoulseekRs::LockPoisoned => {
Self::LockPoisoned => {
write!(f, "Lock poisoned by panicking thread")
}
}
@@ -51,7 +51,7 @@ impl fmt::Display for SoulseekRs {
impl Error for SoulseekRs {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
SoulseekRs::NetworkError(err) => Some(err),
Self::NetworkError(err) => Some(err),
_ => None,
}
}
@@ -59,19 +59,19 @@ impl Error for SoulseekRs {
impl From<std::io::Error> for SoulseekRs {
fn from(err: std::io::Error) -> Self {
SoulseekRs::NetworkError(err)
Self::NetworkError(err)
}
}
impl From<std::num::ParseIntError> for SoulseekRs {
fn from(err: std::num::ParseIntError) -> Self {
SoulseekRs::ParseError(format!("Integer parse error: {}", err))
Self::ParseError(format!("Integer parse error: {err}"))
}
}
impl From<String> for SoulseekRs {
fn from(err: String) -> Self {
SoulseekRs::CompressionError(err)
Self::CompressionError(err)
}
}
+2
View File
@@ -18,6 +18,7 @@ impl<Op> Default for Handlers<Op> {
}
impl<Op> Handlers<Op> {
#[must_use]
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
@@ -31,6 +32,7 @@ impl<Op> Handlers<Op> {
self.handlers.insert(handler.get_code(), Box::new(handler));
self
}
#[must_use]
pub fn get_handler(
&self,
code: u8,
@@ -18,13 +18,15 @@ impl Default for MessageReader {
}
impl MessageReader {
pub fn new() -> Self {
#[must_use]
pub const fn new() -> Self {
Self {
buffer: VecDeque::new(),
}
}
#[cfg(test)]
#[must_use]
pub fn new_with_buffer(buffer: Vec<u8>) -> Self {
Self {
buffer: buffer.into(),
@@ -47,6 +49,7 @@ impl MessageReader {
Ok(())
}
#[must_use]
pub fn buffer_len(&self) -> usize {
self.buffer.len()
}
+22 -18
View File
@@ -10,7 +10,7 @@ pub use message_reader::MessageReader;
use std::str;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
pub enum MessageType {
Server,
@@ -30,7 +30,7 @@ impl std::fmt::Display for Error {
impl std::error::Error for Error {}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Message {
data: Vec<u8>,
pointer: usize,
@@ -43,7 +43,8 @@ impl Default for Message {
}
impl Message {
pub fn new() -> Self {
#[must_use]
pub const fn new() -> Self {
Self {
data: Vec::new(),
pointer: 0,
@@ -51,8 +52,8 @@ impl Message {
}
pub fn print_hex(&self) {
let data = &self.data;
const BYTES_PER_LINE: usize = 16;
let data = &self.data;
let chunks = data.chunks(BYTES_PER_LINE);
for (i, chunk) in chunks.enumerate() {
@@ -95,6 +96,7 @@ impl Message {
}
#[allow(dead_code)]
#[must_use]
pub fn get_message_code_u32(&self) -> u32 {
if self.data.len() < 8 {
return 0;
@@ -102,6 +104,7 @@ impl Message {
u32::from_le_bytes(self.data[4..8].try_into().unwrap())
}
#[must_use]
pub fn get_message_code(&self) -> u8 {
if self.data.len() <= 4 {
return 0;
@@ -109,6 +112,7 @@ impl Message {
self.data[4]
}
#[must_use]
pub fn get_message_code_send(&self) -> u8 {
if self.data.is_empty() {
return 0;
@@ -116,34 +120,38 @@ impl Message {
self.data[0]
}
pub fn new_with_data(data: Vec<u8>) -> Self {
#[must_use]
pub const fn new_with_data(data: Vec<u8>) -> Self {
Self { data, pointer: 0 }
}
#[allow(dead_code)]
pub fn reset_pointer(&mut self) {
pub const fn reset_pointer(&mut self) {
self.pointer = 0;
}
pub fn set_pointer(&mut self, pointer: usize) {
pub const fn set_pointer(&mut self, pointer: usize) {
self.pointer = pointer;
}
pub fn get_pointer(&mut self) -> usize {
pub const fn get_pointer(&mut self) -> usize {
self.pointer
}
pub fn get_size(&mut self) -> usize {
pub const fn get_size(&mut self) -> usize {
self.data.len()
}
#[must_use]
pub fn get_data(&self) -> Vec<u8> {
self.data.clone()
}
#[must_use]
pub fn get_slice(&self, from: usize, to: usize) -> Vec<u8> {
self.data[from..to].to_vec()
}
/// gets buffer with the message length prepended
#[must_use]
pub fn get_buffer(&self) -> Vec<u8> {
let mut b = vec![0u8; 4];
let length = self.data.len() as u32;
@@ -277,7 +285,7 @@ impl Message {
}
pub fn write_bool(&mut self, value: bool) -> &mut Self {
self.data.push(if value { 1 } else { 0 });
self.data.push(u8::from(value));
self
}
@@ -333,16 +341,13 @@ impl Message {
104 => Ok("WishlistInterval"),
160 => Ok("ExcludedSearchPhrases"),
1001 => Ok("CantConnectToPeer"),
_ => {
Err(Error(format!("Unknown server message code: {}", code)))
}
_ => Err(Error(format!("Unknown server message code: {code}"))),
},
MessageType::PeerInit => match code {
0 => Ok("PierceFireWall"),
1 => Ok("PeerInit"),
_ => Err(Error(format!(
"Unknown peer init message code: {}",
code
"Unknown peer init message code: {code}"
))),
},
MessageType::Peer => match code {
@@ -361,7 +366,7 @@ impl Message {
46 => Ok("UploadFailed"),
50 => Ok("UploadDenied"),
51 => Ok("PlaceInQueueRequest"),
_ => Err(Error(format!("Unknown peer message code: {}", code))),
_ => Err(Error(format!("Unknown peer message code: {code}"))),
},
MessageType::Distributed => match code {
3 => Ok("SearchRequest"),
@@ -369,8 +374,7 @@ impl Message {
5 => Ok("BranchRoot"),
93 => Ok("EmbeddedMessage"),
_ => Err(Error(format!(
"Unknown distributed message code: {}",
code
"Unknown distributed message code: {code}"
))),
},
}
@@ -9,9 +9,9 @@ impl MessageHandler<PeerMessage> for FileSearchResponse {
9
}
fn handle(&self, message: &mut Message, sender: Sender<PeerMessage>) {
let file_search = match SearchResult::new_from_message(message) {
Ok(result) => result,
Err(_) => return, // Skip malformed search results
// Skip malformed search results
let Ok(file_search) = SearchResult::new_from_message(message) else {
return;
};
sender
@@ -22,7 +22,7 @@ impl MessageHandler<ServerMessage> for GetPeerAddressHandler {
let port = message.read_int32();
let obfuscation_type = message.read_int32();
let obfuscated_port = message.read_int32() as u16;
println!("GetPeerAddressHandler: {:?}", username); // Debug print
println!("GetPeerAddressHandler: {username:?}"); // Debug print
sender
.send(ServerMessage::GetPeerAddressResponse {
@@ -4,6 +4,7 @@ use crate::{
pub struct MessageFactory;
impl MessageFactory {
#[must_use]
pub fn build_get_peer_address(username: &str) -> Message {
let mut message = Message::new();
@@ -11,6 +12,7 @@ impl MessageFactory {
message.write_string(username);
message
}
#[must_use]
pub fn build_login_message(username: &str, password: &str) -> Message {
// Message::new_with_data(
// [
@@ -42,6 +44,7 @@ impl MessageFactory {
.clone()
}
#[must_use]
pub fn build_shared_folders_message(
folder_count: u32,
file_count: u32,
@@ -52,6 +55,7 @@ impl MessageFactory {
.write_int32(file_count)
.clone()
}
#[must_use]
pub fn build_file_search_message(token: u32, query: &str) -> Message {
Message::new()
.write_int32(26)
@@ -59,21 +63,25 @@ impl MessageFactory {
.write_string(query)
.clone()
}
#[must_use]
pub fn build_set_status_message(status_code: u32) -> Message {
Message::new()
.write_int32(28)
.write_int32(status_code)
.clone()
}
#[must_use]
pub fn build_no_parent_message() -> Message {
Message::new().write_int32(71).write_bool(true).clone()
}
#[must_use]
pub fn build_set_wait_port_message(port: u16) -> Message {
Message::new()
.write_int32(2)
.write_int32(port.into())
.clone()
}
#[must_use]
pub fn build_watch_user(token: u32) -> Message {
Message::new()
.write_raw_bytes([5, 0, 0, 0, 0].to_vec())
@@ -81,6 +89,7 @@ impl MessageFactory {
.clone()
}
#[must_use]
pub fn build_queue_upload_message(filename: &str) -> Message {
Message::new()
.write_int32(43)
@@ -88,6 +97,7 @@ impl MessageFactory {
.clone()
}
#[must_use]
pub fn build_transfer_request_message(
filename: &str,
token: u32,
@@ -99,6 +109,7 @@ impl MessageFactory {
.write_string(filename)
.clone()
}
#[must_use]
pub fn build_transfer_response_message(transfer: Transfer) -> Message {
Message::new()
.write_int32(41)
@@ -106,6 +117,7 @@ impl MessageFactory {
.write_bool(true)
.clone()
}
#[must_use]
pub fn build_pierce_firewall_message(token: u32) -> Message {
Message::new()
.write_int8(0) // PierceFirewall message code
@@ -114,6 +126,7 @@ impl MessageFactory {
}
#[allow(dead_code)]
#[must_use]
pub fn build_peer_init_message(
own_username: &str,
connection_type: ConnectionType,
@@ -134,7 +147,7 @@ fn test_build_watch_user() {
let message = MessageFactory::build_watch_user(token);
let expect: Vec<u8> = [5, 0, 0, 0, 0, 223, 0, 0, 0].to_vec();
assert_eq!(expect, message.get_data())
assert_eq!(expect, message.get_data());
}
#[test]
@@ -151,7 +164,7 @@ fn test_build_login_message() {
]
.to_vec();
assert_eq!(expect, message.get_data())
assert_eq!(expect, message.get_data());
}
#[test]
@@ -162,5 +175,5 @@ fn test_build_file_search_message() {
119, 97, 120,
]
.to_vec();
assert_eq!(expect, message.get_data())
assert_eq!(expect, message.get_data());
}
@@ -20,12 +20,12 @@ impl MessageHandler<ServerMessage> for MessageUser {
let user_message = UserMessage::new(
id,
timestamp,
username.clone(),
username,
message_content,
new_message,
);
info!("[MessageUser] User message received:{:?}", user_message);
user_message.print()
user_message.print();
}
}
+17 -19
View File
@@ -37,22 +37,22 @@ pub enum DownloadError {
impl std::fmt::Display for DownloadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ConnectionFailed(e) => write!(f, "Connection failed: {}", e),
Self::ConnectionFailed(e) => write!(f, "Connection failed: {e}"),
Self::InvalidAddress(addr) => {
write!(f, "Invalid address: {}", addr)
write!(f, "Invalid address: {addr}")
}
Self::HandshakeFailed(e) => write!(f, "Handshake failed: {}", e),
Self::StreamReadError(e) => write!(f, "Stream read error: {}", e),
Self::StreamWriteError(e) => write!(f, "Stream write error: {}", e),
Self::HandshakeFailed(e) => write!(f, "Handshake failed: {e}"),
Self::StreamReadError(e) => write!(f, "Stream read error: {e}"),
Self::StreamWriteError(e) => write!(f, "Stream write error: {e}"),
Self::TokenNotFound(token) => {
write!(f, "Token not found: {}", token)
write!(f, "Token not found: {token}")
}
Self::DownloadInfoMissing(token) => {
write!(f, "Download info missing for token: {}", token)
write!(f, "Download info missing for token: {token}")
}
Self::FileWriteError(e) => write!(f, "File write error: {}", e),
Self::FileWriteError(e) => write!(f, "File write error: {e}"),
Self::PathResolutionError(msg) => {
write!(f, "Path resolution error: {}", msg)
write!(f, "Path resolution error: {msg}")
}
Self::InvalidTokenBytes => {
write!(f, "Invalid token bytes received")
@@ -100,7 +100,7 @@ struct StreamProcessor {
}
impl StreamProcessor {
fn new() -> Self {
const fn new() -> Self {
Self {
total_bytes: 0,
received: false,
@@ -113,7 +113,7 @@ impl StreamProcessor {
self.total_bytes += data.len();
}
fn should_continue(&self, expected_size: Option<usize>) -> bool {
const fn should_continue(&self, expected_size: Option<usize>) -> bool {
if let Some(size) = expected_size {
self.total_bytes < size
} else {
@@ -134,7 +134,7 @@ pub struct DownloadPeer {
impl DownloadPeer {
#[must_use]
pub fn new(
pub const fn new(
username: String,
host: String,
port: u32,
@@ -277,7 +277,7 @@ impl DownloadPeer {
loop {
if let Some(ref dl) = download {
self.wait_while_paused(client_context, dl)?;
Self::wait_while_paused(client_context, dl)?;
}
match stream.read(&mut read_buffer) {
@@ -387,7 +387,6 @@ impl DownloadPeer {
}
fn wait_while_paused(
&self,
client_context: &Arc<RwLock<ClientContext>>,
download: &Download,
) -> Result<(), DownloadError> {
@@ -408,7 +407,6 @@ impl DownloadPeer {
}
fn resolve_download_path(
&self,
download: &Download,
) -> Result<String, DownloadError> {
let download_directory = &download.download_directory;
@@ -462,7 +460,6 @@ impl DownloadPeer {
}
fn save_downloaded_file(
&self,
path: &str,
data: &[u8],
) -> Result<(), DownloadError> {
@@ -511,8 +508,8 @@ impl DownloadPeer {
let (buffer, download) =
self.read_download_stream(&mut stream, &client_context, download)?;
let final_path = self.resolve_download_path(&download)?;
self.save_downloaded_file(&final_path, &buffer)?;
let final_path = Self::resolve_download_path(&download)?;
Self::save_downloaded_file(&final_path, &buffer)?;
trace!(
"[download_peer:{}] download completed successfully: {} bytes, saved to: {}",
@@ -533,7 +530,8 @@ mod tests {
fn finalize_rejects_truncated_download() {
// Peer closed early: 5 of 10 promised bytes. Must be a failure so the
// partial file is never reported as Completed.
let result = DownloadPeer::finalize_download_buffer(vec![1, 2, 3, 4, 5], 10);
let result =
DownloadPeer::finalize_download_buffer(vec![1, 2, 3, 4, 5], 10);
assert!(matches!(
result,
Err(DownloadError::IncompleteDownload {
+7 -4
View File
@@ -65,7 +65,7 @@ fn parse_token_from_buffer(buffer: &[u8], username: &str) -> Option<u32> {
let token = u32::from_le_bytes(
token_bytes
.try_into()
.unwrap_or_else(|_| panic!("[listener:{}] slice with incorrect length, can't extract transfer_token", username)),
.unwrap_or_else(|_| panic!("[listener:{username}] slice with incorrect length, can't extract transfer_token")),
);
Some(token)
}
@@ -182,7 +182,7 @@ fn handle_file_connection(
DownloadStatus::Completed,
),
Err(e) => {
error!("[listener] handle_file_connection write: {}", e)
error!("[listener] handle_file_connection write: {}", e);
}
}
info!(
@@ -204,7 +204,10 @@ fn handle_file_connection(
DownloadStatus::Failed,
),
Err(e) => {
error!("[listener] handle_file_connection fail write: {}", e)
error!(
"[listener] handle_file_connection fail write: {}",
e
);
}
}
}
@@ -272,7 +275,7 @@ fn handle_incoming_connection(stream: TcpStream, context: ConnectionContext) {
&context,
&peer_ip,
peer_port,
)
);
});
}
ConnectionType::D => {
+9 -8
View File
@@ -60,9 +60,9 @@ impl FromStr for ConnectionType {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"P" => Ok(ConnectionType::P),
"F" => Ok(ConnectionType::F),
"D" => Ok(ConnectionType::D),
"P" => Ok(Self::P),
"F" => Ok(Self::F),
"D" => Ok(Self::D),
_ => Err(ParseConnectionTypeError),
}
}
@@ -71,11 +71,11 @@ impl FromStr for ConnectionType {
impl fmt::Display for ConnectionType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
ConnectionType::P => "P",
ConnectionType::F => "F",
ConnectionType::D => "D",
Self::P => "P",
Self::F => "F",
Self::D => "D",
};
write!(f, "{}", s)
write!(f, "{s}")
}
}
@@ -93,7 +93,8 @@ pub struct Peer {
}
impl Peer {
#[allow(clippy::too_many_arguments, dead_code)]
pub fn new(
#[must_use]
pub const fn new(
username: String,
connection_type: ConnectionType,
host: String,
+6 -3
View File
@@ -109,7 +109,8 @@ pub struct Download {
}
impl Download {
pub fn is_finished(&self) -> bool {
#[must_use]
pub const fn is_finished(&self) -> bool {
matches!(
self.status,
DownloadStatus::Completed
@@ -118,7 +119,8 @@ impl Download {
)
}
pub fn bytes_downloaded(&self) -> u64 {
#[must_use]
pub const fn bytes_downloaded(&self) -> u64 {
match &self.status {
DownloadStatus::InProgress {
bytes_downloaded, ..
@@ -131,7 +133,8 @@ impl Download {
}
}
pub fn speed_bytes_per_sec(&self) -> f64 {
#[must_use]
pub const fn speed_bytes_per_sec(&self) -> f64 {
match &self.status {
DownloadStatus::InProgress {
speed_bytes_per_sec,
+33 -57
View File
@@ -32,12 +32,10 @@ pub fn init() {
unsafe {
LOG_LEVEL = match level.as_str() {
"ERROR" => LogLevel::Error,
"WARN" => LogLevel::Warn,
"INFO" => LogLevel::Info,
"DEBUG" => LogLevel::Debug,
"DEBUG" | "VERBOSE" => LogLevel::Debug, // Map VERBOSE to DEBUG
"TRACE" => LogLevel::Trace,
"VERBOSE" => LogLevel::Debug, // Map VERBOSE to DEBUG
_ => LogLevel::Warn, // Default to WARN
_ => LogLevel::Warn, // "WARN" or default
};
}
@@ -54,10 +52,7 @@ pub fn init() {
}
}
Err(e) => {
eprintln!(
"Failed to open log file '{}': {}",
log_file_path, e
);
eprintln!("Failed to open log file '{log_file_path}': {e}");
}
}
}
@@ -74,7 +69,7 @@ enum LogSink {
Stderr,
}
fn choose_sink(buffering: bool, has_file: bool) -> LogSink {
const fn choose_sink(buffering: bool, has_file: bool) -> LogSink {
match (buffering, has_file) {
// A configured file always takes the line directly and bypasses
// buffering, so the flush step never re-writes it.
@@ -86,7 +81,7 @@ fn choose_sink(buffering: bool, has_file: bool) -> LogSink {
}
fn has_log_file() -> bool {
LOG_FILE.lock().map(|f| f.is_some()).unwrap_or(false)
LOG_FILE.lock().is_ok_and(|f| f.is_some())
}
pub fn log(level: LogLevel, message: &str) {
@@ -161,29 +156,11 @@ pub fn log(level: LogLevel, message: &str) {
};
let formatted_message = format!(
"[{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}] [{}] {}",
year,
month,
day,
hours,
minutes,
seconds,
subsec_millis,
level_str,
message
"[{year:04}-{month:02}-{day:02} {hours:02}:{minutes:02}:{seconds:02}.{subsec_millis:03}] [{level_str}] {message}"
);
let formatted_message_plain = format!(
"[{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}] [{}] {}",
year,
month,
day,
hours,
minutes,
seconds,
subsec_millis,
level_str_plain,
message
"[{year:04}-{month:02}-{day:02} {hours:02}:{minutes:02}:{seconds:02}.{subsec_millis:03}] [{level_str_plain}] {message}"
);
match choose_sink(BUFFERING.load(Ordering::Relaxed), has_log_file())
@@ -192,18 +169,17 @@ pub fn log(level: LogLevel, message: &str) {
if let Ok(mut log_file) = LOG_FILE.lock()
&& let Some(file) = log_file.as_mut()
{
let _ =
writeln!(file, "{}", formatted_message_plain);
let _ = writeln!(file, "{formatted_message_plain}");
let _ = file.flush();
}
}
LogSink::Buffer => {
if let Ok(mut buffer) = BUFFER.lock() {
buffer.push(formatted_message.clone());
buffer.push(formatted_message);
}
}
LogSink::Stderr => {
eprintln!("{}", formatted_message);
eprintln!("{formatted_message}");
}
}
}
@@ -228,18 +204,18 @@ pub fn flush_buffered_logs() {
for message in buffer.iter() {
// Strip ANSI codes for file output
let plain_message = strip_ansi_codes(message);
let _ = writeln!(file, "{}", plain_message);
let _ = writeln!(file, "{plain_message}");
}
let _ = file.flush();
} else {
for message in buffer.iter() {
eprintln!("{}", message);
eprintln!("{message}");
}
}
} else {
// If lock fails, fall back to stderr
for message in buffer.iter() {
eprintln!("{}", message);
eprintln!("{message}");
}
}
buffer.clear();
@@ -270,26 +246,6 @@ fn strip_ansi_codes(s: &str) -> String {
result
}
#[cfg(test)]
mod tests {
use super::{LogSink, choose_sink};
#[test]
fn a_configured_file_bypasses_buffering_so_lines_are_not_duplicated() {
// With a file configured, the line goes straight to the file whether or
// not buffering is on, so it is never also queued for a flush that would
// write it a second time.
assert_eq!(choose_sink(true, true), LogSink::File);
assert_eq!(choose_sink(false, true), LogSink::File);
}
#[test]
fn without_a_file_buffering_routes_to_the_buffer_else_stderr() {
assert_eq!(choose_sink(true, false), LogSink::Buffer);
assert_eq!(choose_sink(false, false), LogSink::Stderr);
}
}
#[macro_export]
macro_rules! error {
($($arg:tt)*) => {
@@ -324,3 +280,23 @@ macro_rules! trace {
$crate::utils::logger::log($crate::utils::logger::LogLevel::Trace, &format!($($arg)*))
};
}
#[cfg(test)]
mod tests {
use super::{LogSink, choose_sink};
#[test]
fn a_configured_file_bypasses_buffering_so_lines_are_not_duplicated() {
// With a file configured, the line goes straight to the file whether or
// not buffering is on, so it is never also queued for a flush that would
// write it a second time.
assert_eq!(choose_sink(true, true), LogSink::File);
assert_eq!(choose_sink(false, true), LogSink::File);
}
#[test]
fn without_a_file_buffering_routes_to_the_buffer_else_stderr() {
assert_eq!(choose_sink(true, false), LogSink::Buffer);
assert_eq!(choose_sink(false, false), LogSink::Stderr);
}
}
+13 -12
View File
@@ -22,7 +22,7 @@ use std::convert::TryInto;
* K[i] = floor(2^32 * abs(sin(i+1)))
*/
fn table_construction_function(i: u32) -> u32 {
let x: f64 = i as f64;
let x: f64 = f64::from(i);
let sin_eval = x.sin().abs();
// note: 4294967296 == 2^32
(4294967296.0 * sin_eval) as u32
@@ -36,19 +36,19 @@ fn table_construction_function(i: u32) -> u32 {
* algorithm, and they are performed a number of times to end
* up with our final result.
*/
fn f(x: u32, y: u32, z: u32) -> u32 {
const fn f(x: u32, y: u32, z: u32) -> u32 {
x & y | !x & z
}
fn g(x: u32, y: u32, z: u32) -> u32 {
const fn g(x: u32, y: u32, z: u32) -> u32 {
x & z | y & !z
}
fn h(x: u32, y: u32, z: u32) -> u32 {
const fn h(x: u32, y: u32, z: u32) -> u32 {
x ^ y ^ z
}
fn i(x: u32, y: u32, z: u32) -> u32 {
const fn i(x: u32, y: u32, z: u32) -> u32 {
y ^ (x | !z)
}
@@ -67,7 +67,7 @@ fn vec_to_array<T, const N: usize>(v: Vec<T>) -> [T; N] {
* table[i] is an entry generated by our sine function, s is an unsigned
* integer, and a,b,c,d are our 32 bit registers we perform operations on.
*/
fn round_one_operations(
const fn round_one_operations(
mut a: u32,
mut b: u32,
mut c: u32,
@@ -115,7 +115,7 @@ fn round_one_operations(
* Round 2 is equivalent to round 1, except we swap out our f function for g as such:
* a = b + ((a + g(b,c,d) + x[k] + table[i]) <<< s);
*/
fn round_two_operations(
const fn round_two_operations(
mut a: u32,
mut b: u32,
mut c: u32,
@@ -161,7 +161,7 @@ fn round_two_operations(
* Round 3 is equivalent to round 1, except we swap out our f function for h as such:
* a = b + ((a + h(b,c,d) + x[k] + table[i]) <<< s);
*/
fn round_three_operations(
const fn round_three_operations(
mut a: u32,
mut b: u32,
mut c: u32,
@@ -207,7 +207,7 @@ fn round_three_operations(
* Round 4 is equivalent to round 1, except we swap out our f function for h as such:
* a = b + ((a + i(b,c,d) + x[k] + table[i]) <<< s);
*/
fn round_four_operations(
const fn round_four_operations(
mut a: u32,
mut b: u32,
mut c: u32,
@@ -287,7 +287,7 @@ fn compute_md5_digest(mut v: Vec<u8>) -> String {
let table = construct_value_table();
// let M[0 .. N-1] = words of resulting message, where N is multiple of 16
for chunk in v.chunks_exact_mut(64) {
for chunk in v.as_chunks_mut::<64>().0 {
let x = convert_u8_chunk_to_u32(chunk);
// set all values of a,b,c,d to aa,bb,cc, and dd respectively.
@@ -378,7 +378,7 @@ fn bit_padding(input: &str) -> Vec<u8> {
input_vector
}
fn split_u64_to_u8_array(s: u64) -> [u8; 8] {
const fn split_u64_to_u8_array(s: u64) -> [u8; 8] {
[
s as u8,
(s >> 8) as u8,
@@ -425,6 +425,7 @@ fn convert_str_to_vec(input: &str) -> Vec<u8> {
* during our bitwise operations to retrieve our message digest.
* (This is a simplification, but describes the core functionality)
*/
#[must_use]
pub fn md5(input: &str) -> String {
let input_vec = bit_padding(input);
compute_md5_digest(input_vec)
@@ -454,7 +455,7 @@ fn lowercase_alphabet_correct_hash() {
assert_eq!(
"c3fcd3d76192e4007dfb496cca67e13b",
md5("abcdefghijklmnopqrstuvwxyz")
)
);
}
#[test]
+1
View File
@@ -1,5 +1,6 @@
use std::path::PathBuf;
#[must_use]
pub fn expand_tilde(path: &str) -> PathBuf {
if let Some(stripped) = path.strip_prefix('~')
&& let Ok(home) = std::env::var("HOME")
+36 -35
View File
@@ -16,7 +16,8 @@ pub struct ThreadPool {
}
impl ThreadPool {
pub fn new(size: usize) -> ThreadPool {
#[must_use]
pub fn new(size: usize) -> Self {
assert!(size > 0);
let (sender, receiver) = mpsc::channel();
@@ -27,7 +28,7 @@ impl ThreadPool {
workers.push(Worker::new(Arc::clone(&receiver)));
}
ThreadPool {
Self {
workers,
sender: Some(sender),
}
@@ -62,6 +63,39 @@ impl Drop for ThreadPool {
}
}
struct Worker {
thread: Option<thread::JoinHandle<()>>,
}
impl Worker {
fn new(receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Self {
let thread = thread::spawn(move || {
loop {
let message = match receiver.lock_safe() {
Ok(rx) => rx.recv(),
Err(_) => break,
};
match message {
// Contain a panicking job so it kills only that job, not the
// worker: the pool has a fixed number of workers and a lost
// one is never replaced. The lock is already released here,
// so a panic cannot poison the shared receiver.
Ok(Message::NewJob(job)) => {
let _ = std::panic::catch_unwind(
std::panic::AssertUnwindSafe(job),
);
}
Ok(Message::Terminate) | Err(_) => break,
}
}
});
Self {
thread: Some(thread),
}
}
}
#[cfg(test)]
mod tests {
use super::ThreadPool;
@@ -88,36 +122,3 @@ mod tests {
);
}
}
struct Worker {
thread: Option<thread::JoinHandle<()>>,
}
impl Worker {
fn new(receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
let thread = thread::spawn(move || {
loop {
let message = match receiver.lock_safe() {
Ok(rx) => rx.recv(),
Err(_) => break,
};
match message {
// Contain a panicking job so it kills only that job, not the
// worker: the pool has a fixed number of workers and a lost
// one is never replaced. The lock is already released here,
// so a panic cannot poison the shared receiver.
Ok(Message::NewJob(job)) => {
let _ = std::panic::catch_unwind(
std::panic::AssertUnwindSafe(job),
);
}
Ok(Message::Terminate) | Err(_) => break,
}
}
});
Worker {
thread: Some(thread),
}
}
}
+11 -11
View File
@@ -8,7 +8,7 @@ struct BitReader {
}
impl BitReader {
fn new(mem: Vec<u8>) -> Self {
const fn new(mem: Vec<u8>) -> Self {
Self {
mem,
pos: 0,
@@ -42,7 +42,7 @@ impl BitReader {
fn read_bits(&mut self, n: usize) -> std::result::Result<u32, String> {
let mut o = 0u32;
for i in 0..n {
o |= (self.read_bit()? as u32) << i;
o |= u32::from(self.read_bit()?) << i;
}
Ok(o)
}
@@ -51,7 +51,7 @@ impl BitReader {
// read bytes as an integer in little-endian
let mut o = 0u32;
for i in 0..n {
o |= (self.read_byte()? as u32) << (8 * i);
o |= u32::from(self.read_byte()?) << (8 * i);
}
Ok(o)
}
@@ -72,7 +72,7 @@ pub fn deflate(input: &[u8]) -> Result<Vec<u8>> {
return Err(SoulseekRs::CompressionError("invalid CINFO".to_string()));
}
let flg = r.read_byte()?;
if !((cmf as u32) * 256 + (flg as u32)).is_multiple_of(31) {
if !(u32::from(cmf) * 256 + u32::from(flg)).is_multiple_of(31) {
return Err(SoulseekRs::CompressionError(
"CMF+FLG checksum failed".to_string(),
));
@@ -119,12 +119,12 @@ fn inflate_block_no_compression(
#[derive(Clone)]
struct Node {
symbol: Option<u32>,
left: Option<Box<Node>>,
right: Option<Box<Node>>,
left: Option<Box<Self>>,
right: Option<Box<Self>>,
}
impl Node {
fn new() -> Self {
const fn new() -> Self {
Self {
symbol: None,
left: None,
@@ -138,7 +138,7 @@ struct HuffmanTree {
}
impl HuffmanTree {
fn new() -> Self {
const fn new() -> Self {
Self { root: Node::new() }
}
@@ -175,9 +175,9 @@ fn decode_symbol(
// at a missing child; error out instead of unwrap-panicking.
node = next
.as_ref()
.ok_or("Invalid Huffman code".to_string())?;
.ok_or_else(|| "Invalid Huffman code".to_string())?;
}
node.symbol.ok_or("No symbol found".to_string())
node.symbol.ok_or_else(|| "No symbol found".to_string())
}
const LENGTH_EXTRA_BITS: [usize; 29] = [
@@ -397,7 +397,7 @@ mod tests {
#[test]
fn test_extract_header_success() {
let _data = [120, 156]; // Valid zlib header
// [120, 156] is a valid zlib header
let result = deflate(&[120, 156, 3, 0, 0, 0, 0, 1]); // Minimal valid zlib stream
assert!(result.is_ok());
}