diff --git a/Cargo.lock b/Cargo.lock index eca5b8d..0f88bbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -184,6 +193,12 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" + [[package]] name = "bytes" version = "1.10.1" @@ -440,6 +455,22 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "parking_lot", + "pear", + "serde", + "tempfile", + "toml", + "uncased", + "version_check", +] + [[package]] name = "fnv" version = "1.0.7" @@ -880,6 +911,12 @@ version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "instability" version = "0.3.7" @@ -950,6 +987,7 @@ version = "0.1.0" dependencies = [ "axum", "chrono", + "figment", "julid-rs", "mime", "mime_guess", @@ -1386,6 +1424,29 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1437,6 +1498,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[package]] name = "quote" version = "1.0.40" @@ -1740,6 +1814,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2039,6 +2122,47 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower" version = "0.5.2" @@ -2118,6 +2242,15 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicase" version = "2.8.1" @@ -2578,6 +2711,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + [[package]] name = "winsafe" version = "0.0.19" @@ -2599,6 +2741,12 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yoke" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index a7c058f..956db24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] axum = { version = "0.8", features = ["macros"] } chrono = "0.4" +figment = { version = "0.10", features = ["toml", "test", "env"] } julid-rs = { version = "1", default-features = false, features = ["serde"] } mime = "0.3" mime_guess = "2" diff --git a/src/discovery/http.rs b/src/discovery/http.rs index e40dce6..8219f3a 100644 --- a/src/discovery/http.rs +++ b/src/discovery/http.rs @@ -1,17 +1,16 @@ -use std::{collections::HashMap, net::SocketAddr, sync::Arc}; -use tokio::sync::Mutex; +use std::{net::SocketAddr, sync::Arc}; use axum::{ + Json, extract::{ConnectInfo, State}, - Extension, Json, }; -use crate::{models::device::DeviceInfo, Client}; +use crate::{Client, JoecalState, models::device::DeviceInfo}; impl Client { pub async fn announce_http(&self, ip: Option) -> crate::error::Result<()> { if let Some(ip) = ip { - let url = format!("http://{}/api/localsend/v2/register", ip); + let url = format!("http://{ip}/api/localsend/v2/register"); let client = reqwest::Client::new(); client.post(&url).json(&self.device).send().await?; } @@ -19,16 +18,17 @@ impl Client { } pub async fn announce_http_legacy(&self) -> crate::error::Result<()> { - // send the reqwest to all local ip addresses from 192.168.0.0 to 192.168.255.255 + // send the reqwest to all local ip addresses from 192.168.0.0 to + // 192.168.255.255 let mut address_list = Vec::new(); for j in 0..256 { for k in 0..256 { - address_list.push(format!("192.168.{:03}.{}:53317", j, k)); + address_list.push(format!("192.168.{j:03}.{k}:53317")); } } for ip in address_list { - let url = format!("http://{}/api/localsend/v2/register", ip); + let url = format!("http://{ip}/api/localsend/v2/register"); self.http_client .post(&url) .json(&self.device) @@ -40,16 +40,16 @@ impl Client { } pub async fn register_device( - State(peers): State>>>, - Extension(client): Extension, + State(state): State>, ConnectInfo(addr): ConnectInfo, Json(device): Json, ) -> Json { let mut addr = addr; - addr.set_port(device.port); - peers + addr.set_port(state.device.port); + state + .peers .lock() .await .insert(device.fingerprint.clone(), (addr, device.clone())); - Json(client) + Json(device) } diff --git a/src/discovery/mod.rs b/src/discovery/mod.rs index 437022b..9faa60a 100644 --- a/src/discovery/mod.rs +++ b/src/discovery/mod.rs @@ -1,6 +1,6 @@ use std::net::SocketAddr; -use crate::{models::device::DeviceInfo, Client}; +use crate::{Client, models::device::DeviceInfo}; pub mod http; pub mod multicast; @@ -28,7 +28,8 @@ impl Client { return; } - // Announce in return upon receiving a valid device message and it wants announcements + // Announce in return upon receiving a valid device message and it wants + // announcements if let Err(e) = self.announce_multicast().await { eprintln!("Error during multicast announcement: {}", e); } diff --git a/src/lib.rs b/src/lib.rs index 257b614..b44b1f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,15 +4,17 @@ pub mod models; pub mod server; pub mod transfer; -use crate::models::device::DeviceInfo; -use std::collections::HashMap; -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; -use std::sync::Arc; -use tokio::net::UdpSocket; -use tokio::sync::Mutex; -use tokio::task::JoinHandle; +use std::{ + collections::HashMap, + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, + sync::Arc, +}; + +use tokio::{net::UdpSocket, sync::Mutex, task::JoinHandle}; use transfer::session::Session; +use crate::models::device::DeviceInfo; + #[derive(Clone)] pub struct Client { pub device: DeviceInfo, @@ -25,6 +27,13 @@ pub struct Client { pub download_dir: String, } +#[derive(Clone)] +pub struct JoecalState { + pub device: DeviceInfo, + pub peers: Arc>>, + pub sessions: Arc>>, // Session ID to Session +} + impl Client { pub async fn default() -> crate::error::Result { let device = DeviceInfo::default(); diff --git a/src/main.rs b/src/main.rs index ba39121..3fc6812 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,16 @@ -use joecalsend::{models::device::DeviceInfo, Client}; +use joecalsend::{Client, models::device::DeviceInfo}; +use tokio::io; #[tokio::main] -async fn main() { +async fn main() -> io::Result<()> { let device = DeviceInfo::default(); - dbg!(device); + dbg!(&device); - let client = Client::with_config( - DeviceInfo::default(), - 53317, - "/home/ardent/joecalsend".into(), - ) - .await - .unwrap(); + let client = Client::with_config(device, 53317, "/home/ardent/joecalsend".into()) + .await + .unwrap(); let (h1, h2, h3) = client.start().await.unwrap(); - tokio::join!(h1, h2, h3); + let _ = tokio::join!(h1, h2, h3); + + Ok(()) } diff --git a/src/models/file.rs b/src/models/file.rs index b21cce2..9be7818 100644 --- a/src/models/file.rs +++ b/src/models/file.rs @@ -1,8 +1,9 @@ -use crate::error::LocalSendError; +use std::{path::Path, time::SystemTime}; + use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use std::path::Path; -use std::time::SystemTime; + +use crate::error::LocalSendError; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/src/server/http.rs b/src/server/http.rs index bbf0a2e..3202389 100644 --- a/src/server/http.rs +++ b/src/server/http.rs @@ -1,16 +1,17 @@ +use std::{net::SocketAddr, sync::Arc}; + use axum::{ + Extension, Json, Router, extract::DefaultBodyLimit, routing::{get, post}, - Extension, Json, Router, }; -use std::net::SocketAddr; use tokio::net::TcpListener; use tower_http::limit::RequestBodyLimitLayer; use crate::{ + Client, JoecalState, discovery::http::register_device, transfer::upload::{register_prepare_upload, register_upload}, - Client, }; impl Client { @@ -19,7 +20,7 @@ impl Client { let addr = SocketAddr::from(([0, 0, 0, 0], self.port)); let listener = TcpListener::bind(&addr).await?; - println!("HTTP server listening on {}", addr); + println!("HTTP server listening on {addr}"); axum::serve( listener, @@ -32,6 +33,11 @@ impl Client { fn create_router(&self) -> Router { let peers = self.peers.clone(); let device = self.device.clone(); + let state = Arc::new(JoecalState { + peers, + device: device.clone(), + sessions: self.sessions.clone(), + }); Router::new() .route("/api/localsend/v2/register", post(register_device)) @@ -49,9 +55,7 @@ impl Client { .route("/api/localsend/v2/upload", post(register_upload)) .layer(DefaultBodyLimit::disable()) .layer(RequestBodyLimitLayer::new(1024 * 1024 * 1024)) - .layer(Extension(self.device.clone())) - .layer(Extension(self.sessions.clone())) .layer(Extension(self.download_dir.clone())) - .with_state(peers) + .with_state(state) } } diff --git a/src/transfer/upload.rs b/src/transfer/upload.rs index f1901bd..ae2ef19 100644 --- a/src/transfer/upload.rs +++ b/src/transfer/upload.rs @@ -1,25 +1,24 @@ -use std::collections::HashMap; -use std::net::SocketAddr; -use std::path::PathBuf; -use std::sync::Arc; +use std::{collections::HashMap, net::SocketAddr, path::PathBuf, sync::Arc}; -use axum::body::Bytes; -use axum::extract::{ConnectInfo, Query}; -use axum::http::StatusCode; -use axum::Extension; -use axum::{response::IntoResponse, Json}; - -use crate::error::{LocalSendError, Result}; -use crate::transfer::session::{Session, SessionStatus}; -use crate::{ - models::{device::DeviceInfo, file::FileMetadata}, - Client, +use axum::{ + Extension, Json, + body::Bytes, + extract::{ConnectInfo, Query, State}, + http::StatusCode, + response::IntoResponse, }; use julid::Julid; use native_dialog::MessageDialogBuilder; use serde::{Deserialize, Serialize}; use tokio::sync::Mutex; +use crate::{ + Client, JoecalState, + error::{LocalSendError, Result}, + models::{device::DeviceInfo, file::FileMetadata}, + transfer::session::{Session, SessionStatus}, +}; + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PrepareUploadResponse { @@ -176,8 +175,7 @@ impl Client { } pub async fn register_prepare_upload( - Extension(client): Extension, - Extension(sessions): Extension>>>, + State(state): State>, ConnectInfo(addr): ConnectInfo, Json(req): Json, ) -> impl IntoResponse { @@ -203,13 +201,17 @@ pub async fn register_prepare_upload( session_id: session_id.clone(), files: req.files.clone(), file_tokens: file_tokens.clone(), - receiver: client.clone(), + receiver: state.device.clone(), sender: req.info.clone(), status: SessionStatus::Active, addr, }; - sessions.lock().await.insert(session_id.clone(), session); + state + .sessions + .lock() + .await + .insert(session_id.clone(), session); return ( StatusCode::OK, @@ -226,7 +228,7 @@ pub async fn register_prepare_upload( pub async fn register_upload( Query(params): Query, - Extension(sessions): Extension>>>, + State(state): State>, Extension(download_dir): Extension, body: Bytes, ) -> impl IntoResponse { @@ -236,7 +238,7 @@ pub async fn register_upload( let token = ¶ms.token; // Get session and validate - let mut sessions_lock = sessions.lock().await; + let mut sessions_lock = state.sessions.lock().await; let session = match sessions_lock.get_mut(session_id) { Some(session) => session, None => return StatusCode::BAD_REQUEST.into_response(), @@ -259,7 +261,7 @@ pub async fn register_upload( StatusCode::INTERNAL_SERVER_ERROR, "File not found".to_string(), ) - .into_response() + .into_response(); } };