From d2ac69816567f092fe0d4661723bc43778cb481b Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Mon, 1 Dec 2025 14:31:32 +0000 Subject: fix cargo clippy and fmt warnings --- src/config.rs | 8 +--- src/git/handlers.rs | 1 - src/git/mod.rs | 13 +++--- src/git/protocol.rs | 18 ++++---- src/git/subprocess.rs | 34 ++++++-------- src/http/mod.rs | 125 ++++++++++++++++++++++++++++++-------------------- src/http/nip11.rs | 34 +++++++------- src/nostr/builder.rs | 14 +++--- src/nostr/events.rs | 18 ++++---- 9 files changed, 140 insertions(+), 125 deletions(-) (limited to 'src') diff --git a/src/config.rs b/src/config.rs index f04b7d8..9b0d0b8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,8 +5,10 @@ use std::env; /// Database backend type for the relay #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum DatabaseBackend { /// In-memory database (default, fastest, no persistence) + #[default] Memory, /// NostrDB backend (persistent, optimized for Nostr) NostrDb, @@ -14,12 +16,6 @@ pub enum DatabaseBackend { Lmdb, } -impl Default for DatabaseBackend { - fn default() -> Self { - Self::Memory - } -} - impl std::str::FromStr for DatabaseBackend { type Err = anyhow::Error; diff --git a/src/git/handlers.rs b/src/git/handlers.rs index 00f2449..e84cabb 100644 --- a/src/git/handlers.rs +++ b/src/git/handlers.rs @@ -5,7 +5,6 @@ use http_body_util::Full; use hyper::{body::Bytes, Response, StatusCode}; use nostr_relay_builder::prelude::MemoryDatabase; -use nostr_sdk::EventId; use std::path::PathBuf; use std::sync::Arc; use tokio::io::{AsyncReadExt, AsyncWriteExt}; diff --git a/src/git/mod.rs b/src/git/mod.rs index 494f8b9..a783782 100644 --- a/src/git/mod.rs +++ b/src/git/mod.rs @@ -306,11 +306,7 @@ pub fn parse_git_url(path: &str) -> Option<(&str, &str, &str)> { let subpath = parts[2]; // Extract identifier (remove .git suffix if present for the middle part) - let identifier = if repo_part.ends_with(".git") { - &repo_part[..repo_part.len() - 4] - } else { - repo_part - }; + let identifier = repo_part.strip_suffix(".git").unwrap_or(repo_part); Some((npub, identifier, subpath)) } @@ -343,7 +339,12 @@ mod tests { // Initialize bare repository Command::new("git") - .args(["init", "--bare", "--initial-branch=main", bare_repo.to_str().unwrap()]) + .args([ + "init", + "--bare", + "--initial-branch=main", + bare_repo.to_str().unwrap(), + ]) .output() .unwrap(); diff --git a/src/git/protocol.rs b/src/git/protocol.rs index 93177de..8592c27 100644 --- a/src/git/protocol.rs +++ b/src/git/protocol.rs @@ -55,11 +55,11 @@ impl PktLine { return Err(ProtocolError::InsufficientData); } - let len_str = std::str::from_utf8(&input[0..4]) - .map_err(|_| ProtocolError::InvalidLength)?; - - let len = u16::from_str_radix(len_str, 16) - .map_err(|_| ProtocolError::InvalidLength)? as usize; + let len_str = + std::str::from_utf8(&input[0..4]).map_err(|_| ProtocolError::InvalidLength)?; + + let len = + u16::from_str_radix(len_str, 16).map_err(|_| ProtocolError::InvalidLength)? as usize; if len == 0 { // Flush packet @@ -81,19 +81,19 @@ impl PktLine { /// Parse all pkt-lines from bytes pub fn parse_all(mut input: &[u8]) -> Result, ProtocolError> { let mut packets = Vec::new(); - + while !input.is_empty() { let (packet, remaining) = Self::parse(input)?; let is_flush = matches!(packet, PktLine::Flush); packets.push(packet); input = remaining; - + // Stop at flush packet if is_flush { break; } } - + Ok(packets) } } @@ -259,4 +259,4 @@ mod tests { "application/x-git-upload-pack-result" ); } -} \ No newline at end of file +} diff --git a/src/git/subprocess.rs b/src/git/subprocess.rs index c95bce5..2d9a981 100644 --- a/src/git/subprocess.rs +++ b/src/git/subprocess.rs @@ -28,9 +28,9 @@ impl GitSubprocess { advertise: bool, ) -> std::io::Result { let repo_path = repo_path.as_ref(); - + let mut cmd = Command::new("git"); - + // GRASP-01 requirement: MUST include `allow-reachable-sha1-in-want` and // `allow-tip-sha1-in-want` in advertisement and serve available oids. // These config options must be passed before the command name. @@ -38,22 +38,22 @@ impl GitSubprocess { cmd.arg("uploadpack.allowReachableSHA1InWant=true"); cmd.arg("-c"); cmd.arg("uploadpack.allowTipSHA1InWant=true"); - + cmd.arg(service.command_name()); - + if advertise { cmd.arg("--advertise-refs"); } - + cmd.arg("--stateless-rpc"); cmd.arg(repo_path); - + cmd.stdin(Stdio::piped()); cmd.stdout(Stdio::piped()); cmd.stderr(Stdio::piped()); - + let child = cmd.spawn()?; - + Ok(Self { child }) } @@ -101,8 +101,8 @@ impl GitSubprocess { #[cfg(test)] mod tests { use super::*; - use tempfile::TempDir; use std::process::Command as StdCommand; + use tempfile::TempDir; fn create_bare_repo() -> TempDir { let dir = TempDir::new().unwrap(); @@ -118,11 +118,8 @@ mod tests { #[tokio::test] async fn test_spawn_upload_pack_advertise() { let repo = create_bare_repo(); - let mut proc = GitSubprocess::spawn( - GitService::UploadPack, - repo.path(), - true, - ).expect("Failed to spawn git"); + let mut proc = GitSubprocess::spawn(GitService::UploadPack, repo.path(), true) + .expect("Failed to spawn git"); // Should have spawned successfully assert!(proc.stdout().is_some()); @@ -135,15 +132,12 @@ mod tests { #[tokio::test] async fn test_spawn_receive_pack() { let repo = create_bare_repo(); - let mut proc = GitSubprocess::spawn( - GitService::ReceivePack, - repo.path(), - false, - ).expect("Failed to spawn git"); + let mut proc = GitSubprocess::spawn(GitService::ReceivePack, repo.path(), false) + .expect("Failed to spawn git"); assert!(proc.stdout().is_some()); assert!(proc.stdin().is_some()); let _ = proc.kill().await; } -} \ No newline at end of file +} diff --git a/src/http/mod.rs b/src/http/mod.rs index 07b47ee..f43cf86 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -9,20 +9,20 @@ use std::net::SocketAddr; use std::pin::Pin; use std::sync::Arc; +use base64::Engine; +use http_body_util::{BodyExt, Full}; use hyper::body::{Bytes, Incoming}; use hyper::header::{CONNECTION, SEC_WEBSOCKET_ACCEPT, UPGRADE}; use hyper::server::conn::http1; use hyper::service::Service; use hyper::{Method, Request, Response}; use hyper_util::rt::TokioIo; -use http_body_util::{BodyExt, Full}; +use nostr_relay_builder::prelude::MemoryDatabase; +use nostr_relay_builder::LocalRelay; use nostr_sdk::hashes::sha1::Hash as Sha1Hash; use nostr_sdk::hashes::{Hash, HashEngine}; use nostr_sdk::PublicKey; -use nostr_relay_builder::prelude::MemoryDatabase; -use nostr_relay_builder::LocalRelay; use tokio::net::TcpListener; -use base64::Engine; use crate::config::Config; use crate::git; @@ -50,7 +50,12 @@ struct HttpService { } impl HttpService { - fn new(relay: LocalRelay, config: Config, remote: SocketAddr, database: Arc) -> Self { + fn new( + relay: LocalRelay, + config: Config, + remote: SocketAddr, + database: Arc, + ) -> Self { Self { relay, config, @@ -77,10 +82,12 @@ impl Service> for HttpService { // GRASP-01 spec line 47: Respond to OPTIONS with 204 No Content if method == Method::OPTIONS { return Box::pin(async move { - Ok(add_cors_headers(Response::builder().header("server", "ngit-grasp")) - .status(204) - .body(Full::new(Bytes::new())) - .unwrap()) + Ok( + add_cors_headers(Response::builder().header("server", "ngit-grasp")) + .status(204) + .body(Full::new(Bytes::new())) + .unwrap(), + ) }); } @@ -89,41 +96,47 @@ impl Service> for HttpService { let npub = npub.to_string(); let identifier = identifier.to_string(); let subpath = subpath.to_string(); - - tracing::debug!("Git request: {} {} (npub={}, id={}, subpath={})", - method, path, npub, identifier, subpath); + + tracing::debug!( + "Git request: {} {} (npub={}, id={}, subpath={})", + method, + path, + npub, + identifier, + subpath + ); let repo_path = git::resolve_repo_path(&git_data_path, &npub, &identifier); return Box::pin(async move { // Collect request body once before the match statement - let body_bytes = req.collect().await + let body_bytes = req + .collect() + .await .map(|collected| collected.to_bytes()) .unwrap_or_else(|_| Bytes::new()); - + let result = match (method.as_ref(), subpath.as_str()) { // GET /info/refs?service=git-upload-pack or git-receive-pack (m, sp) if m == Method::GET && sp.starts_with("info/refs") => { // Parse query string for service parameter - let service = query.as_deref().unwrap_or("") + let service = query + .as_deref() + .unwrap_or("") .strip_prefix("service=") .and_then(git::protocol::GitService::from_query_param); match service { - Some(svc) => { - git::handlers::handle_info_refs(repo_path, svc).await - } - None => { - Err(git::handlers::GitError::RepositoryNotFound) - } + Some(svc) => git::handlers::handle_info_refs(repo_path, svc).await, + None => Err(git::handlers::GitError::RepositoryNotFound), } } - + // POST /git-upload-pack (clone/fetch) (m, "git-upload-pack") if m == Method::POST => { git::handlers::handle_upload_pack(repo_path, body_bytes).await } - + // POST /git-receive-pack (push) - with GRASP authorization via database (m, "git-receive-pack") if m == Method::POST => { // Convert npub (bech32) to hex pubkey for authorization @@ -137,33 +150,41 @@ impl Service> for HttpService { .unwrap()); } }; - + git::handlers::handle_receive_pack( repo_path, body_bytes.clone(), Some(database.clone()), &identifier, &owner_pubkey_hex, - ).await - } - - _ => { - Err(git::handlers::GitError::RepositoryNotFound) + ) + .await } + + _ => Err(git::handlers::GitError::RepositoryNotFound), }; match result { Ok(response) => { // Add CORS headers to successful Git responses let (parts, body) = response.into_parts(); - Ok(add_cors_headers(Response::builder() - .status(parts.status)) - .header("content-type", parts.headers.get("content-type") - .and_then(|v| v.to_str().ok()) - .unwrap_or("application/octet-stream")) - .header("cache-control", parts.headers.get("cache-control") - .and_then(|v| v.to_str().ok()) - .unwrap_or("no-cache")) + Ok(add_cors_headers(Response::builder().status(parts.status)) + .header( + "content-type", + parts + .headers + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"), + ) + .header( + "cache-control", + parts + .headers + .get("cache-control") + .and_then(|v| v.to_str().ok()) + .unwrap_or("no-cache"), + ) .body(body) .unwrap()) } @@ -191,15 +212,20 @@ impl Service> for HttpService { tracing::error!("Failed to serialize NIP-11 document: {}", e); "{}".to_string() }); - - tracing::debug!("Serving NIP-11 relay information document to {}", self.remote); - + + tracing::debug!( + "Serving NIP-11 relay information document to {}", + self.remote + ); + return Box::pin(async move { - Ok(add_cors_headers(Response::builder().header("server", "ngit-grasp")) - .status(200) - .header("content-type", "application/nostr+json") - .body(Full::new(Bytes::from(json))) - .unwrap()) + Ok( + add_cors_headers(Response::builder().header("server", "ngit-grasp")) + .status(200) + .header("content-type", "application/nostr+json") + .body(Full::new(Bytes::from(json))) + .unwrap(), + ) }); } } @@ -221,12 +247,13 @@ impl Service> for HttpService { let addr = self.remote; let relay = self.relay.clone(); - + tokio::spawn(async move { match hyper::upgrade::on(req).await { Ok(upgraded) => { tracing::info!("WebSocket connection established from {}", addr); - if let Err(e) = relay.take_connection(TokioIo::new(upgraded), addr).await + if let Err(e) = + relay.take_connection(TokioIo::new(upgraded), addr).await { tracing::error!("Relay error for {}: {}", addr, e); } @@ -288,12 +315,12 @@ pub async fn run_server( tracing::info!("Domain: {}", config.domain); let listener = TcpListener::bind(&bind_addr).await?; - + loop { let (socket, addr) = listener.accept().await?; let io = TokioIo::new(socket); let service = HttpService::new(relay.clone(), config.clone(), addr, database.clone()); - + tokio::spawn(async move { if let Err(e) = http1::Builder::new() .serve_connection(io, service) diff --git a/src/http/nip11.rs b/src/http/nip11.rs index a93ee5f..593ef9a 100644 --- a/src/http/nip11.rs +++ b/src/http/nip11.rs @@ -1,10 +1,9 @@ +use crate::config::Config; /// NIP-11 Relay Information Document /// /// Implements NIP-11 relay information endpoint with GRASP-01 extensions. /// See: https://github.com/nostr-protocol/nips/blob/master/11.md - use serde::{Deserialize, Serialize}; -use crate::config::Config; /// NIP-11 Relay Information Document /// @@ -14,37 +13,36 @@ use crate::config::Config; pub struct RelayInformationDocument { /// Relay name pub name: String, - + /// Relay description pub description: String, - + /// Relay owner's public key (hex format) #[serde(skip_serializing_if = "Option::is_none")] pub pubkey: Option, - + /// Contact information for relay admin #[serde(skip_serializing_if = "Option::is_none")] pub contact: Option, - + /// List of NIPs supported by this relay pub supported_nips: Vec, - + /// Relay software identifier pub software: String, - + /// Software version pub version: String, - + // GRASP-01 Extensions (lines 11-14 of GRASP-01 spec) - /// List of supported GRASPs (e.g., ["GRASP-01"]) /// Required by GRASP-01 specification line 12 pub supported_grasps: Vec, - + /// Repository acceptance criteria description /// Required by GRASP-01 specification line 13 pub repo_acceptance_criteria: String, - + /// Curation policy (present if curated, absent otherwise) /// Optional per GRASP-01 specification line 14 #[serde(skip_serializing_if = "Option::is_none")] @@ -66,7 +64,7 @@ impl RelayInformationDocument { ], software: env!("CARGO_PKG_NAME").to_string(), version: env!("CARGO_PKG_VERSION").to_string(), - + // GRASP-01 Extensions supported_grasps: vec!["GRASP-01".to_string()], repo_acceptance_criteria: format!( @@ -77,7 +75,7 @@ impl RelayInformationDocument { curation: None, // Not a curated relay - only SPAM prevention via GRASP-01 policy } } - + /// Serialize to JSON string pub fn to_json(&self) -> Result { serde_json::to_string_pretty(self) @@ -102,7 +100,7 @@ mod tests { }; let doc = RelayInformationDocument::from_config(&config); - + assert_eq!(doc.name, "Test Relay"); assert_eq!(doc.description, "A test relay"); assert_eq!(doc.pubkey, Some("npub1test".to_string())); @@ -129,7 +127,7 @@ mod tests { let doc = RelayInformationDocument::from_config(&config); let json = doc.to_json().expect("Failed to serialize to JSON"); - + // Verify JSON contains expected fields assert!(json.contains("\"name\"")); assert!(json.contains("\"description\"")); @@ -137,10 +135,10 @@ mod tests { assert!(json.contains("\"supported_grasps\"")); assert!(json.contains("\"repo_acceptance_criteria\"")); assert!(json.contains("GRASP-01")); - + // Verify it's valid JSON by parsing let parsed: serde_json::Value = serde_json::from_str(&json).expect("Invalid JSON"); assert_eq!(parsed["name"], "Test Relay"); assert_eq!(parsed["supported_grasps"][0], "GRASP-01"); } -} \ No newline at end of file +} diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs index 8e9926a..97fd17e 100644 --- a/src/nostr/builder.rs +++ b/src/nostr/builder.rs @@ -51,7 +51,7 @@ impl Nip34WritePolicy { /// Create a bare git repository if it doesn't exist /// Path format: //.git fn ensure_bare_repository(&self, announcement: &RepositoryAnnouncement) -> Result<(), String> { - let repo_path = self.git_data_path.join(&announcement.repo_path()); + let repo_path = self.git_data_path.join(announcement.repo_path()); // Check if repository already exists if repo_path.exists() { @@ -69,7 +69,7 @@ impl Nip34WritePolicy { // Initialize bare repository using git command let output = std::process::Command::new("git") - .args(&["init", "--bare", repo_path.to_str().unwrap()]) + .args(["init", "--bare", repo_path.to_str().unwrap()]) .output() .map_err(|e| format!("Failed to execute git init: {}", e))?; @@ -482,7 +482,7 @@ impl Nip34WritePolicy { }; // Build repository path - let repo_path = self.git_data_path.join(&announcement.repo_path()); + let repo_path = self.git_data_path.join(announcement.repo_path()); // Validate the ref match git::validate_nostr_ref(&repo_path, &event_id, &expected_commit) { @@ -631,8 +631,8 @@ impl Nip34WritePolicy { let kind_u16 = event.kind.as_u16(); // Check if this is any kind of replaceable event - let is_regular_replaceable = kind_u16 >= 10000 && kind_u16 < 20000; - let is_parameterized_replaceable = kind_u16 >= 30000 && kind_u16 < 40000; + let is_regular_replaceable = (10000..20000).contains(&kind_u16); + let is_parameterized_replaceable = (30000..40000).contains(&kind_u16); if is_regular_replaceable || is_parameterized_replaceable { // Build the appropriate address format based on event type @@ -669,7 +669,7 @@ impl Nip34WritePolicy { ]; for tag_type in &addressable_tags { - let filter = Filter::new().custom_tag(tag_type.clone(), address.clone()); + let filter = Filter::new().custom_tag(*tag_type, address.clone()); match database.query(filter).await { Ok(events) => { @@ -691,7 +691,7 @@ impl Nip34WritePolicy { ]; for tag_type in &event_id_tags { - let filter = Filter::new().custom_tag(tag_type.clone(), event_id_hex.clone()); + let filter = Filter::new().custom_tag(*tag_type, event_id_hex.clone()); match database.query(filter).await { Ok(events) => { diff --git a/src/nostr/events.rs b/src/nostr/events.rs index 6a62ccd..050bfdd 100644 --- a/src/nostr/events.rs +++ b/src/nostr/events.rs @@ -322,9 +322,9 @@ impl RepositoryState { /// Get the HEAD branch name (without refs/heads/ prefix) pub fn get_head_branch(&self) -> Option<&str> { - self.head.as_ref().and_then(|h| { - h.strip_prefix("refs/heads/") - }) + self.head + .as_ref() + .and_then(|h| h.strip_prefix("refs/heads/")) } /// Check if the HEAD commit is available in the git repository @@ -397,7 +397,7 @@ pub fn validate_state(event: &Event) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use nostr_sdk::{EventBuilder, Keys, Tag}; + use nostr_sdk::{EventBuilder, Keys}; fn create_test_keys() -> Keys { Keys::generate() @@ -618,7 +618,10 @@ mod tests { let announcement = RepositoryAnnouncement::from_event(event).unwrap(); assert_eq!(announcement.maintainers.len(), 1); - assert_eq!(announcement.maintainers[0], maintainer_keys.public_key().to_hex()); + assert_eq!( + announcement.maintainers[0], + maintainer_keys.public_key().to_hex() + ); } #[test] @@ -727,10 +730,7 @@ mod tests { let keys = create_test_keys(); let tags = vec![ - Tag::custom( - nostr_sdk::TagKind::d(), - vec!["test-repo".to_string()], - ), + Tag::custom(nostr_sdk::TagKind::d(), vec!["test-repo".to_string()]), Tag::custom( nostr_sdk::TagKind::Custom("refs/heads/main".into()), vec!["a1b2c3d4".to_string()], -- cgit v1.2.3