From 7846f91afe4e0be4ae70bbd3a95bd84dc903be7f Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 29 Jul 2026 19:17:16 +0000 Subject: [PATCH] Format with rustfmt --- src/bot/commands.rs | 8 ++- src/bot/modules/builtins.rs | 18 +++++- src/bot/modules/hash.rs | 29 +++++++-- src/bot/modules/weather.rs | 115 +++++++++++++++++++++++++++--------- src/config.rs | 12 +++- src/irc/encoding.rs | 15 +++-- src/main.rs | 4 +- tests/hash.rs | 19 +++++- tests/modules.rs | 19 +++++- tests/protocol.rs | 10 +++- tests/weather.rs | 14 ++++- 11 files changed, 209 insertions(+), 54 deletions(-) diff --git a/src/bot/commands.rs b/src/bot/commands.rs index 2b26c1d..a2316e6 100644 --- a/src/bot/commands.rs +++ b/src/bot/commands.rs @@ -8,7 +8,9 @@ const HISTORY_GRACE_SECS: u64 = 60; impl Bot { pub(super) fn handle_batch(&mut self, msg: &Message) { - let Some(reference) = msg.param(0) else { return }; + let Some(reference) = msg.param(0) else { + return; + }; if let Some(name) = reference.strip_prefix('+') { self.batches .insert(name.to_string(), msg.param(1).unwrap_or("").to_string()); @@ -38,7 +40,9 @@ impl Bot { return Ok(()); } - let Some(target) = msg.param(0) else { return Ok(()) }; + let Some(target) = msg.param(0) else { + return Ok(()); + }; let text = msg.trailing().unwrap_or(""); let sender = msg.nick().unwrap_or(""); diff --git a/src/bot/modules/builtins.rs b/src/bot/modules/builtins.rs index 2626f94..e7a7b15 100644 --- a/src/bot/modules/builtins.rs +++ b/src/bot/modules/builtins.rs @@ -3,9 +3,21 @@ use crate::bot::module::{Action, Command, CommandSpec, Module}; pub struct Builtins; const COMMANDS: &[CommandSpec] = &[ - CommandSpec { name: "ping", usage: "ping", about: "reply with 'pong'" }, - CommandSpec { name: "echo", usage: "echo ", about: "echo the text back" }, - CommandSpec { name: "hello", usage: "hello", about: "greet the sender" }, + CommandSpec { + name: "ping", + usage: "ping", + about: "reply with 'pong'", + }, + CommandSpec { + name: "echo", + usage: "echo ", + about: "echo the text back", + }, + CommandSpec { + name: "hello", + usage: "hello", + about: "greet the sender", + }, ]; impl Module for Builtins { diff --git a/src/bot/modules/hash.rs b/src/bot/modules/hash.rs index 985db90..29a5a55 100644 --- a/src/bot/modules/hash.rs +++ b/src/bot/modules/hash.rs @@ -3,10 +3,26 @@ use crate::bot::module::{Action, Command, CommandSpec, Module}; pub struct Hash; const COMMANDS: &[CommandSpec] = &[ - CommandSpec { name: "md5", usage: "md5 ", about: "MD5 hex digest of " }, - CommandSpec { name: "sha1", usage: "sha1 ", about: "SHA1 hex digest of " }, - CommandSpec { name: "sha256", usage: "sha256 ", about: "SHA256 hex digest of " }, - CommandSpec { name: "hash", usage: "hash ", about: "md5, sha1 and sha256 of " }, + CommandSpec { + name: "md5", + usage: "md5 ", + about: "MD5 hex digest of ", + }, + CommandSpec { + name: "sha1", + usage: "sha1 ", + about: "SHA1 hex digest of ", + }, + CommandSpec { + name: "sha256", + usage: "sha256 ", + about: "SHA256 hex digest of ", + }, + CommandSpec { + name: "hash", + usage: "hash ", + about: "md5, sha1 and sha256 of ", + }, ]; impl Module for Hash { @@ -21,7 +37,10 @@ impl Module for Hash { fn on_command(&mut self, cmd: &Command) -> Vec { let input = cmd.args.join(" "); if input.is_empty() { - return vec![Action::reply(format!("usage: {}{} ", cmd.prefix, cmd.name))]; + return vec![Action::reply(format!( + "usage: {}{} ", + cmd.prefix, cmd.name + ))]; } let b = input.as_bytes(); let reply = match cmd.name { diff --git a/src/bot/modules/weather.rs b/src/bot/modules/weather.rs index d8b5c06..43c2d67 100644 --- a/src/bot/modules/weather.rs +++ b/src/bot/modules/weather.rs @@ -14,8 +14,16 @@ use crate::bot::module::{Action, Command, CommandSpec, Module}; const CACHE_TTL: Duration = Duration::from_secs(600); const COMMANDS: &[CommandSpec] = &[ - CommandSpec { name: "add", usage: "add ", about: "save your weather location" }, - CommandSpec { name: "w", usage: "w [location]", about: "weather for your saved location" }, + CommandSpec { + name: "add", + usage: "add ", + about: "save your weather location", + }, + CommandSpec { + name: "w", + usage: "w [location]", + about: "weather for your saved location", + }, ]; pub struct Weather { @@ -35,7 +43,11 @@ impl Weather { } pub fn with_path(path: PathBuf) -> Weather { - Weather { store: Store::load(&path), path, cache: HashMap::new() } + Weather { + store: Store::load(&path), + path, + cache: HashMap::new(), + } } pub fn location_of(&self, nick: &str) -> Option<&str> { @@ -56,15 +68,27 @@ impl Module for Weather { match cmd.name { "add" => { if cmd.args.is_empty() { - return vec![Action::reply(format!("usage: {}add ", cmd.prefix))]; + return vec![Action::reply(format!( + "usage: {}add ", + cmd.prefix + ))]; } let loc = cmd.args.join(" "); self.store.set(cmd.sender, &loc); if let Err(e) = self.store.save(&self.path) { - crate::log(&format!("weather: could not save {}: {e}", self.path.display())); - return vec![Action::reply(format!("{}: could not save location", cmd.sender))]; + crate::log(&format!( + "weather: could not save {}: {e}", + self.path.display() + )); + return vec![Action::reply(format!( + "{}: could not save location", + cmd.sender + ))]; } - vec![Action::reply(format!("{}: saved {loc} — try {}w", cmd.sender, cmd.prefix))] + vec![Action::reply(format!( + "{}: saved {loc} — try {}w", + cmd.sender, cmd.prefix + ))] } "w" => { let loc = if !cmd.args.is_empty() { @@ -104,7 +128,9 @@ impl Store { fn load(path: &Path) -> Store { match fs::read_to_string(path) { Ok(s) => Store { map: parse_db(&s) }, - Err(_) => Store { map: BTreeMap::new() }, + Err(_) => Store { + map: BTreeMap::new(), + }, } } @@ -130,7 +156,11 @@ fn fetch_weather(location: &str) -> Result { } match https_get("wttr.in", &path) { Ok(body) => { - let raw = body.lines().map(str::trim).find(|l| !l.is_empty()).unwrap_or(""); + let raw = body + .lines() + .map(str::trim) + .find(|l| !l.is_empty()) + .unwrap_or(""); if !raw.is_empty() { return Ok(format_weather(raw, location)); } @@ -174,32 +204,52 @@ struct HttpErr { fn https_get(host: &str, path: &str) -> Result { let addr = (host, 443) .to_socket_addrs() - .map_err(|e| HttpErr { msg: e.to_string(), retry: false })? + .map_err(|e| HttpErr { + msg: e.to_string(), + retry: false, + })? .next() - .ok_or(HttpErr { msg: "dns lookup failed".to_string(), retry: false })?; - let stream = TcpStream::connect_timeout(&addr, Duration::from_secs(4)) - .map_err(|e| HttpErr { retry: retryable_io(&e), msg: e.to_string() })?; + .ok_or(HttpErr { + msg: "dns lookup failed".to_string(), + retry: false, + })?; + let stream = + TcpStream::connect_timeout(&addr, Duration::from_secs(4)).map_err(|e| HttpErr { + retry: retryable_io(&e), + msg: e.to_string(), + })?; stream.set_read_timeout(Some(Duration::from_secs(5))).ok(); stream.set_write_timeout(Some(Duration::from_secs(5))).ok(); - let connector = TlsConnector::new().map_err(|e| HttpErr { msg: e.to_string(), retry: false })?; - let mut tls = connector - .connect(host, stream) - .map_err(|e| HttpErr { msg: e.to_string(), retry: true })?; + let connector = TlsConnector::new().map_err(|e| HttpErr { + msg: e.to_string(), + retry: false, + })?; + let mut tls = connector.connect(host, stream).map_err(|e| HttpErr { + msg: e.to_string(), + retry: true, + })?; let req = format!( "GET {path} HTTP/1.0\r\nHost: {host}\r\nUser-Agent: curl/rubot\r\nAccept: text/plain\r\nConnection: close\r\n\r\n" ); - tls.write_all(req.as_bytes()) - .map_err(|e| HttpErr { retry: retryable_io(&e), msg: e.to_string() })?; + tls.write_all(req.as_bytes()).map_err(|e| HttpErr { + retry: retryable_io(&e), + msg: e.to_string(), + })?; let mut buf = Vec::new(); - tls.read_to_end(&mut buf) - .map_err(|e| HttpErr { retry: retryable_io(&e), msg: e.to_string() })?; + tls.read_to_end(&mut buf).map_err(|e| HttpErr { + retry: retryable_io(&e), + msg: e.to_string(), + })?; let text = String::from_utf8_lossy(&buf); let Some((head, body)) = text.split_once("\r\n\r\n") else { - return Err(HttpErr { msg: "malformed response".to_string(), retry: true }); + return Err(HttpErr { + msg: "malformed response".to_string(), + retry: true, + }); }; let code = head .lines() @@ -210,13 +260,18 @@ fn https_get(host: &str, path: &str) -> Result { .and_then(|c| c.parse::().ok()) .unwrap_or(0); if !(200..300).contains(&code) { - return Err(HttpErr { retry: code == 429 || code >= 500, msg: format!("http {code}") }); + return Err(HttpErr { + retry: code == 429 || code >= 500, + msg: format!("http {code}"), + }); } Ok(body.to_string()) } fn retryable_io(e: &io::Error) -> bool { - use io::ErrorKind::{BrokenPipe, ConnectionAborted, ConnectionRefused, ConnectionReset, UnexpectedEof}; + use io::ErrorKind::{ + BrokenPipe, ConnectionAborted, ConnectionRefused, ConnectionReset, UnexpectedEof, + }; matches!( e.kind(), ConnectionReset | ConnectionAborted | ConnectionRefused | BrokenPipe | UnexpectedEof @@ -227,7 +282,9 @@ fn url_encode(s: &str) -> String { let mut out = String::new(); for b in s.bytes() { match b { - b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => out.push(b as char), + b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => { + out.push(b as char) + } _ => out.push_str(&format!("%{b:02X}")), } } @@ -259,13 +316,17 @@ pub fn parse_db(s: &str) -> BTreeMap { } _ => break, } - let Some(key) = parse_string(&mut chars) else { break }; + let Some(key) = parse_string(&mut chars) else { + break; + }; skip_ws(&mut chars); if chars.next() != Some(':') { break; } skip_ws(&mut chars); - let Some(value) = parse_string(&mut chars) else { break }; + let Some(value) = parse_string(&mut chars) else { + break; + }; map.insert(key, value); } map diff --git a/src/config.rs b/src/config.rs index af5289e..55210d6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -78,7 +78,11 @@ pub fn parse_str(content: &str) -> Vec { assemble(base, sections, &None) } -fn assemble(base: Config, sections: Vec<(String, Vec)>, path: &Option) -> Vec { +fn assemble( + base: Config, + sections: Vec<(String, Vec)>, + path: &Option, +) -> Vec { let mut configs = Vec::new(); if sections.is_empty() { let mut c = base; @@ -125,7 +129,11 @@ fn parse_sections(content: &str) -> (Vec, Vec<(String, Vec)>) { log(&format!("line {lineno}: ignoring line without '='")); continue; }; - let entry = (lineno, key.trim().to_ascii_lowercase(), value.trim().to_string()); + let entry = ( + lineno, + key.trim().to_ascii_lowercase(), + value.trim().to_string(), + ); match sections.last_mut() { Some((_, entries)) => entries.push(entry), None => toplevel.push(entry), diff --git a/src/irc/encoding.rs b/src/irc/encoding.rs index 5fc0587..c6d3d56 100644 --- a/src/irc/encoding.rs +++ b/src/irc/encoding.rs @@ -1,8 +1,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; pub fn base64_encode(input: &[u8]) -> String { - const ALPHABET: &[u8; 64] = - b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let mut out = String::with_capacity(input.len().div_ceil(3) * 4); for chunk in input.chunks(3) { let b0 = chunk[0] as u32; @@ -11,8 +10,16 @@ pub fn base64_encode(input: &[u8]) -> String { let n = (b0 << 16) | (b1 << 8) | b2; out.push(ALPHABET[(n >> 18 & 63) as usize] as char); out.push(ALPHABET[(n >> 12 & 63) as usize] as char); - out.push(if chunk.len() > 1 { ALPHABET[(n >> 6 & 63) as usize] as char } else { '=' }); - out.push(if chunk.len() > 2 { ALPHABET[(n & 63) as usize] as char } else { '=' }); + out.push(if chunk.len() > 1 { + ALPHABET[(n >> 6 & 63) as usize] as char + } else { + '=' + }); + out.push(if chunk.len() > 2 { + ALPHABET[(n & 63) as usize] as char + } else { + '=' + }); } out } diff --git a/src/main.rs b/src/main.rs index f85853b..03bab84 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,9 @@ fn supervise(config: Config, labelled: bool) { loop { match run_once(&config, labelled) { Ok(()) => { - log(&format!("{tag}server closed the connection; reconnecting shortly")); + log(&format!( + "{tag}server closed the connection; reconnecting shortly" + )); backoff = 1; } Err(e) => log(&format!("{tag}session error: {e}; retrying in {backoff}s")), diff --git a/tests/hash.rs b/tests/hash.rs index 49b9351..abb374c 100644 --- a/tests/hash.rs +++ b/tests/hash.rs @@ -2,7 +2,14 @@ use rustbot::bot::module::{Action, Command, Module}; use rustbot::bot::modules::hash::{md5, sha1, sha256, Hash}; fn cmd<'a>(name: &'a str, args: &'a [&'a str]) -> Command<'a> { - Command { sender: "u", reply_to: "#c", name, args, prefix: ">", network: "t" } + Command { + sender: "u", + reply_to: "#c", + name, + args, + prefix: ">", + network: "t", + } } fn reply(actions: &[Action]) -> &str { @@ -65,8 +72,14 @@ fn module_all_hashes() { let mut m = Hash; let r = reply(&m.on_command(&cmd("hash", &["abc"]))).to_string(); assert!(r.contains("md5: 900150983cd24fb0d6963f7d28e17f72"), "{r}"); - assert!(r.contains("sha1: a9993e364706816aba3e25717850c26c9cd0d89d"), "{r}"); - assert!(r.contains("sha256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"), "{r}"); + assert!( + r.contains("sha1: a9993e364706816aba3e25717850c26c9cd0d89d"), + "{r}" + ); + assert!( + r.contains("sha256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"), + "{r}" + ); } #[test] diff --git a/tests/modules.rs b/tests/modules.rs index b202e46..60998f2 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -2,7 +2,14 @@ use rustbot::bot::module::{Action, Command, Module}; use rustbot::bot::modules::{builtins::Builtins, dice::Dice}; fn cmd<'a>(name: &'a str, args: &'a [&'a str]) -> Command<'a> { - Command { sender: "alice", reply_to: "#chan", name, args, prefix: ">", network: "test" } + Command { + sender: "alice", + reply_to: "#chan", + name, + args, + prefix: ">", + network: "test", + } } fn reply(actions: &[Action]) -> &str { @@ -21,7 +28,10 @@ fn builtins_ping_pongs() { #[test] fn builtins_echo_joins_args() { let mut m = Builtins; - assert_eq!(reply(&m.on_command(&cmd("echo", &["hello", "world"]))), "hello world"); + assert_eq!( + reply(&m.on_command(&cmd("echo", &["hello", "world"]))), + "hello world" + ); } #[test] @@ -43,7 +53,10 @@ fn dice_2d6_total_is_in_range() { for _ in 0..300 { let r = reply(&d.on_command(&cmd("roll", &["2d6"]))).to_string(); let total: u64 = r.rsplit('=').next().unwrap().trim().parse().unwrap(); - assert!((2..=12).contains(&total), "2d6 total {total} out of range: {r:?}"); + assert!( + (2..=12).contains(&total), + "2d6 total {total} out of range: {r:?}" + ); } } diff --git a/tests/protocol.rs b/tests/protocol.rs index c25ad0a..920ce19 100644 --- a/tests/protocol.rs +++ b/tests/protocol.rs @@ -61,8 +61,14 @@ fn base64_matches_known_vectors() { #[test] fn server_time_parses_to_unix_seconds() { assert_eq!(parse_server_time("1970-01-01T00:00:00.000Z"), Some(0)); - assert_eq!(parse_server_time("2020-01-01T00:00:00Z"), Some(1_577_836_800)); - assert_eq!(parse_server_time("2021-01-01T00:00:00.123Z"), Some(1_609_459_200)); + assert_eq!( + parse_server_time("2020-01-01T00:00:00Z"), + Some(1_577_836_800) + ); + assert_eq!( + parse_server_time("2021-01-01T00:00:00.123Z"), + Some(1_609_459_200) + ); assert_eq!(parse_server_time("not-a-timestamp"), None); } diff --git a/tests/weather.rs b/tests/weather.rs index 9744f06..bf2a850 100644 --- a/tests/weather.rs +++ b/tests/weather.rs @@ -5,7 +5,14 @@ use rustbot::bot::module::{Action, Command, Module}; use rustbot::bot::modules::weather::{format_weather, parse_db, to_json, Weather}; fn cmd<'a>(sender: &'a str, name: &'a str, args: &'a [&'a str]) -> Command<'a> { - Command { sender, reply_to: "#chan", name, args, prefix: ">", network: "test" } + Command { + sender, + reply_to: "#chan", + name, + args, + prefix: ">", + network: "test", + } } fn reply(actions: &[Action]) -> &str { @@ -36,7 +43,10 @@ fn parse_db_tolerates_junk() { assert!(parse_db("").is_empty()); assert!(parse_db("not json").is_empty()); assert_eq!(parse_db("{}").len(), 0); - assert_eq!(parse_db("{\"a\":\"b\"}").get("a").map(String::as_str), Some("b")); + assert_eq!( + parse_db("{\"a\":\"b\"}").get("a").map(String::as_str), + Some("b") + ); } #[test]