From 842373b7c114d2098dd9959eed3b160714fd293b Mon Sep 17 00:00:00 2001 From: Gnome! Date: Sat, 22 Jun 2024 14:51:16 +0100 Subject: [PATCH] Use `ArrayString` in more places via `aformat!` (#2902) This also bumps `to-arraystring` up to 0.2, required for `aformat`. --- .github/workflows/ci.yml | 4 +- Cargo.toml | 5 ++- src/builder/bot_auth_parameters.rs | 1 - src/gateway/shard.rs | 7 ++- src/http/client.rs | 3 +- src/internal/prelude.rs | 2 + src/model/id.rs | 4 +- src/model/mention.rs | 20 ++++----- src/model/misc.rs | 2 - src/utils/argument_convert/mod.rs | 7 ++- src/utils/formatted_timestamp.rs | 68 +++++++++++++++++++++--------- src/utils/mod.rs | 18 +++++++- src/utils/quick_modal.rs | 2 - 13 files changed, 91 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a6f42ae0a2..5bfc75bb629 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,8 @@ name: CI on: [push, pull_request] env: - rust_min: 1.74.0 - rust_nightly: nightly-2024-02-08 + rust_min: 1.79.0 + rust_nightly: nightly-2024-06-21 jobs: test: diff --git a/Cargo.toml b/Cargo.toml index 638e76e7914..1ea2782ecc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" repository = "https://github.com/serenity-rs/serenity.git" version = "0.12.2" edition = "2021" -rust-version = "1.74" +rust-version = "1.79" include = ["src/**/*", "LICENSE.md", "README.md", "CHANGELOG.md", "build.rs"] [workspace] @@ -36,8 +36,9 @@ small-fixed-array = { version = "0.4", features = ["serde"] } bool_to_bitflags = { version = "0.1.2" } nonmax = { version = "0.5.5", features = ["serde"] } strum = { version = "0.26", features = ["derive"] } -to-arraystring = "0.1.0" +to-arraystring = "0.2.0" extract_map = { version = "0.1.0", features = ["serde", "iter_mut"] } +aformat = "0.1.3" # Optional dependencies fxhash = { version = "0.2.1", optional = true } chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"], optional = true } diff --git a/src/builder/bot_auth_parameters.rs b/src/builder/bot_auth_parameters.rs index 961a6aa7ea8..90920e58d89 100644 --- a/src/builder/bot_auth_parameters.rs +++ b/src/builder/bot_auth_parameters.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use arrayvec::ArrayVec; -use to_arraystring::ToArrayString; use url::Url; #[cfg(feature = "http")] diff --git a/src/gateway/shard.rs b/src/gateway/shard.rs index 5f9fb0814ab..69c6cfd13a9 100644 --- a/src/gateway/shard.rs +++ b/src/gateway/shard.rs @@ -770,10 +770,9 @@ impl Shard { } async fn connect(base_url: &str) -> Result { - let url = - Url::parse(&format!("{base_url}?v={}", constants::GATEWAY_VERSION)).map_err(|why| { - warn!("Error building gateway URL with base `{}`: {:?}", base_url, why); - + let url = Url::parse(&aformat!("{}?v={}", CapStr::<64>(base_url), constants::GATEWAY_VERSION)) + .map_err(|why| { + warn!("Error building gateway URL with base `{base_url}`: {why:?}"); Error::Gateway(GatewayError::BuildingUrl) })?; diff --git a/src/http/client.rs b/src/http/client.rs index fd758438cc7..507404fd382 100644 --- a/src/http/client.rs +++ b/src/http/client.rs @@ -14,7 +14,6 @@ use reqwest::{Client, ClientBuilder, Response as ReqwestResponse, StatusCode}; use secrecy::{ExposeSecret as _, Secret}; use serde::de::DeserializeOwned; use serde_json::{from_value, json, to_string, to_vec}; -use to_arraystring::ToArrayString as _; use tracing::{debug, trace}; use super::multipart::{Multipart, MultipartUpload}; @@ -219,7 +218,7 @@ fn parse_token(token: &str) -> Arc { if token.starts_with("Bot ") || token.starts_with("Bearer ") { Arc::from(token) } else { - Arc::from(format!("Bot {token}")) + Arc::from(aformat!("Bot {}", CapStr::<128>(token)).as_str()) } } diff --git a/src/internal/prelude.rs b/src/internal/prelude.rs index 53593b74d36..82a62d064c8 100644 --- a/src/internal/prelude.rs +++ b/src/internal/prelude.rs @@ -4,9 +4,11 @@ pub use std::result::Result as StdResult; +pub use aformat::{aformat, aformat_into, ArrayString, CapStr}; pub use extract_map::{ExtractKey, ExtractMap, LendingIterator}; pub use serde_json::Value; pub use small_fixed_array::{FixedArray, FixedString, TruncatingInto}; +pub use to_arraystring::ToArrayString; pub(crate) use super::utils::join_to_string; pub use crate::error::{Error, Result}; diff --git a/src/model/id.rs b/src/model/id.rs index d5dccf98107..7f56f5feaa5 100644 --- a/src/model/id.rs +++ b/src/model/id.rs @@ -3,9 +3,9 @@ use std::fmt; use nonmax::NonMaxU64; -use to_arraystring::ToArrayString; use super::Timestamp; +use crate::internal::prelude::*; macro_rules! newtype_display_impl { ($name:ident) => { @@ -116,6 +116,8 @@ macro_rules! id_u64 { impl ToArrayString for $name { type ArrayString = ::ArrayString; + const MAX_LENGTH: usize = ::MAX_LENGTH; + fn to_arraystring(self) -> Self::ArrayString { self.get().to_arraystring() } diff --git a/src/model/mention.rs b/src/model/mention.rs index 824aab9034a..ecda0701a03 100644 --- a/src/model/mention.rs +++ b/src/model/mention.rs @@ -4,9 +4,8 @@ use std::fmt; #[cfg(all(feature = "model", feature = "utils"))] use std::str::FromStr; -use to_arraystring::ToArrayString; - use super::prelude::*; +use crate::internal::prelude::*; #[cfg(all(feature = "model", feature = "utils"))] use crate::utils; @@ -113,19 +112,16 @@ impl fmt::Display for Mention { } impl ToArrayString for Mention { - type ArrayString = arrayvec::ArrayString<{ 20 + 4 }>; + const MAX_LENGTH: usize = 20 + 4; + type ArrayString = ArrayString<{ 20 + 4 }>; fn to_arraystring(self) -> Self::ArrayString { - let (prefix, id) = match self { - Self::Channel(id) => ("<#", id.get()), - Self::Role(id) => ("<@&", id.get()), - Self::User(id) => ("<@", id.get()), - }; - let mut out = Self::ArrayString::new(); - out.push_str(prefix); - out.push_str(&id.to_arraystring()); - out.push('>'); + match self { + Self::Channel(id) => aformat_into!(out, "<#{id}>"), + Self::Role(id) => aformat_into!(out, "<@&{id}>"), + Self::User(id) => aformat_into!(out, "<@{id}>"), + }; out } diff --git a/src/model/misc.rs b/src/model/misc.rs index ef2d66858b1..0ccf69d2d58 100644 --- a/src/model/misc.rs +++ b/src/model/misc.rs @@ -8,8 +8,6 @@ use std::fmt::Write; use std::result::Result as StdResult; use std::str::FromStr; -use arrayvec::ArrayString; - use super::prelude::*; use crate::internal::prelude::*; #[cfg(all(feature = "model", any(feature = "cache", feature = "utils")))] diff --git a/src/utils/argument_convert/mod.rs b/src/utils/argument_convert/mod.rs index ce811a71657..6cd56b76dfd 100644 --- a/src/utils/argument_convert/mod.rs +++ b/src/utils/argument_convert/mod.rs @@ -22,7 +22,7 @@ pub use role::*; mod emoji; pub use emoji::*; -use super::DOMAINS; +use super::{DOMAINS, MAX_DOMAIN_LEN}; use crate::model::prelude::*; use crate::prelude::*; @@ -124,8 +124,11 @@ pub fn parse_message_id_pair(s: &str) -> Option<(ChannelId, MessageId)> { /// ``` #[must_use] pub fn parse_message_url(s: &str) -> Option<(GuildId, ChannelId, MessageId)> { + use aformat::{aformat, CapStr}; + for domain in DOMAINS { - if let Some(parts) = s.strip_prefix(&format!("https://{domain}/channels/")) { + let prefix = aformat!("https://{}/channels/", CapStr::(domain)); + if let Some(parts) = s.strip_prefix(prefix.as_str()) { let mut parts = parts.splitn(3, '/'); let guild_id = parts.next()?.parse().ok()?; diff --git a/src/utils/formatted_timestamp.rs b/src/utils/formatted_timestamp.rs index 20701e64d35..6c1343cdc64 100644 --- a/src/utils/formatted_timestamp.rs +++ b/src/utils/formatted_timestamp.rs @@ -2,7 +2,10 @@ use std::error::Error as StdError; use std::fmt; use std::str::FromStr; +use aformat::{ArrayString, ToArrayString}; + use crate::all::Timestamp; +use crate::internal::prelude::*; /// Represents a combination of a timestamp and a style for formatting time in messages. /// @@ -81,17 +84,33 @@ impl From for FormattedTimestamp { } } -impl fmt::Display for FormattedTimestamp { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.style { - Some(style) => write!(f, "", self.timestamp, style), - None => write!(f, "", self.timestamp), +impl ToArrayString for FormattedTimestamp { + const MAX_LENGTH: usize = 27; + type ArrayString = ArrayString<27>; + + fn to_arraystring(self) -> Self::ArrayString { + let mut out = Self::ArrayString::new(); + if let Some(style) = self.style { + aformat_into!(out, "", self.timestamp, style); + } else { + aformat_into!(out, "", self.timestamp); } + + out } } -impl fmt::Display for FormattedTimestampStyle { +impl fmt::Display for FormattedTimestamp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.to_arraystring()) + } +} + +impl ToArrayString for FormattedTimestampStyle { + const MAX_LENGTH: usize = 1; + type ArrayString = ArrayString<1>; + + fn to_arraystring(self) -> Self::ArrayString { let style = match self { Self::ShortTime => "t", Self::LongTime => "T", @@ -101,7 +120,15 @@ impl fmt::Display for FormattedTimestampStyle { Self::LongDateTime => "F", Self::RelativeTime => "R", }; - f.write_str(style) + + ArrayString::from(style) + .expect("One ASCII character should fit into an ArrayString of one capacity") + } +} + +impl fmt::Display for FormattedTimestampStyle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.to_arraystring()) } } @@ -175,12 +202,11 @@ mod tests { let timestamp = Timestamp::now(); let time = FormattedTimestamp::new(timestamp, Some(FormattedTimestampStyle::ShortDateTime)); - - let time_str = time.to_string(); + let time_str = time.to_arraystring(); assert_eq!( time_str, - format!( + aformat!( "", timestamp.unix_timestamp(), FormattedTimestampStyle::ShortDateTime @@ -189,20 +215,20 @@ mod tests { let unstyled = FormattedTimestamp::new(timestamp, None); - let unstyled_str = unstyled.to_string(); + let unstyled_str = unstyled.to_arraystring(); - assert_eq!(unstyled_str, format!("", timestamp.unix_timestamp())); + assert_eq!(&*unstyled_str, &*aformat!("", timestamp.unix_timestamp())); } #[test] fn test_message_time_style() { - assert_eq!(FormattedTimestampStyle::ShortTime.to_string(), "t"); - assert_eq!(FormattedTimestampStyle::LongTime.to_string(), "T"); - assert_eq!(FormattedTimestampStyle::ShortDate.to_string(), "d"); - assert_eq!(FormattedTimestampStyle::LongDate.to_string(), "D"); - assert_eq!(FormattedTimestampStyle::ShortDateTime.to_string(), "f"); - assert_eq!(FormattedTimestampStyle::LongDateTime.to_string(), "F"); - assert_eq!(FormattedTimestampStyle::RelativeTime.to_string(), "R"); + assert_eq!(&*FormattedTimestampStyle::ShortTime.to_arraystring(), "t"); + assert_eq!(&*FormattedTimestampStyle::LongTime.to_arraystring(), "T"); + assert_eq!(&*FormattedTimestampStyle::ShortDate.to_arraystring(), "d"); + assert_eq!(&*FormattedTimestampStyle::LongDate.to_arraystring(), "D"); + assert_eq!(&*FormattedTimestampStyle::ShortDateTime.to_arraystring(), "f"); + assert_eq!(&*FormattedTimestampStyle::LongDateTime.to_arraystring(), "F"); + assert_eq!(&*FormattedTimestampStyle::RelativeTime.to_arraystring(), "R"); } #[test] @@ -211,7 +237,7 @@ mod tests { let time = FormattedTimestamp::new(timestamp, Some(FormattedTimestampStyle::ShortDateTime)); - let time_str = format!( + let time_str = aformat!( "", timestamp.unix_timestamp(), FormattedTimestampStyle::ShortDateTime @@ -223,7 +249,7 @@ mod tests { let unstyled = FormattedTimestamp::new(timestamp, None); - let unstyled_str = format!("", timestamp.unix_timestamp()); + let unstyled_str = aformat!("", timestamp.unix_timestamp()); let unstyled_parsed = unstyled_str.parse::().unwrap(); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 6e62a15dc25..edaaab905e3 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -367,7 +367,7 @@ pub fn parse_quotes(s: &str) -> Vec { } /// Discord's official domains. This is used in [`parse_webhook`] and in its corresponding test. -const DOMAINS: &[&str] = &[ +const DOMAINS: [&str; 6] = [ "discord.com", "canary.discord.com", "ptb.discord.com", @@ -376,6 +376,22 @@ const DOMAINS: &[&str] = &[ "ptb.discordapp.com", ]; +const MAX_DOMAIN_LEN: usize = { + let mut max_len = 0; + let mut i = 0; + + while i < DOMAINS.len() { + let cur_len = DOMAINS[i].len(); + if cur_len > max_len { + max_len = cur_len; + } + + i += 1; + } + + max_len +}; + /// Parses the id and token from a webhook url. Expects a [`url::Url`] rather than a [`&str`]. /// /// # Examples diff --git a/src/utils/quick_modal.rs b/src/utils/quick_modal.rs index 08df17c40b1..af22eb3e6c5 100644 --- a/src/utils/quick_modal.rs +++ b/src/utils/quick_modal.rs @@ -1,7 +1,5 @@ use std::borrow::Cow; -use to_arraystring::ToArrayString; - use crate::builder::{CreateActionRow, CreateInputText, CreateInteractionResponse, CreateModal}; use crate::client::Context; use crate::collector::ModalInteractionCollector;