From 7bb92e1256281bcaf13fed945994df6745a3c020 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 18 Jul 2025 22:22:05 +0200 Subject: [PATCH 1/5] Remove unused serde dependency --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f10f3cc..392c74f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,5 @@ keywords = ["ids", "encode", "short", "sqids", "hashids"] [dependencies] derive_builder = "0.20.2" -serde = "1.0.217" serde_json = "1.0.134" thiserror = "2.0.9" From 18f7f1dfcc7ab8233334bb95a9f4d986d64644d6 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 18 Jul 2025 22:00:01 +0200 Subject: [PATCH 2/5] Replace derive_builder dependency with manual builder implementation Replace the derive by exactly the same API, except the derived impl block is merged with the hand-written impl block for the builder type. Remove the docstrings for the private fields of Sqids that were previously used to generate docs for the builder methods, so there is only one place to update when improving the docs for these things. I'm happy to revert this bit if you prefer having the internal docstrings. --- Cargo.toml | 1 - src/lib.rs | 32 +++++++++++++++++++++++++------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 392c74f..e641e1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,5 @@ readme = "README.md" keywords = ["ids", "encode", "short", "sqids", "hashids"] [dependencies] -derive_builder = "0.20.2" serde_json = "1.0.134" thiserror = "2.0.9" diff --git a/src/lib.rs b/src/lib.rs index 8dac92c..a6e77fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ pub const LICENSE: () = (); use std::{cmp::min, collections::HashSet, result}; -use derive_builder::Builder; use thiserror::Error; /// sqids Error type. @@ -119,15 +118,10 @@ impl Default for Options { } /// A generator for sqids. -#[derive(Clone, Debug, Builder)] -#[builder(build_fn(skip, error = "Error"), pattern = "owned")] +#[derive(Clone, Debug)] pub struct Sqids { - /// The alphabet that is being used when generating sqids. alphabet: Vec, - /// The minimum length of a sqid. min_length: u8, - /// Blocklist. When creating a sqid strings that begins - /// with one of these will be avoided. blocklist: HashSet, } @@ -137,12 +131,36 @@ impl Default for Sqids { } } +/// Builder for [`Sqids`]. +#[derive(Default)] +pub struct SqidsBuilder { + alphabet: Option>, + min_length: Option, + blocklist: Option>, +} + impl SqidsBuilder { /// Create a [SqidsBuilder]. pub fn new() -> Self { Self::default() } + /// The alphabet that is being used when generating sqids. + pub fn alphabet(self, value: Vec) -> Self { + Self { alphabet: Some(value), ..self } + } + + /// The minimum length of a sqid. + pub fn min_length(self, value: u8) -> Self { + Self { min_length: Some(value), ..self } + } + + /// Blocklist. When creating a sqid strings that begins + /// with one of these will be avoided. + pub fn blocklist(self, value: HashSet) -> Self { + Self { blocklist: Some(value), ..self } + } + /// Build a [Sqids] object. pub fn build(self) -> Result { let alphabet: Vec = From 0eae0dbb85036cbc95da1b280e4652bac0ed0e53 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 18 Jul 2025 22:10:34 +0200 Subject: [PATCH 3/5] Remove unnecessary Option<_> on SqidsBuilder#min_length --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a6e77fc..8d6633d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,7 +135,7 @@ impl Default for Sqids { #[derive(Default)] pub struct SqidsBuilder { alphabet: Option>, - min_length: Option, + min_length: u8, blocklist: Option>, } @@ -152,7 +152,7 @@ impl SqidsBuilder { /// The minimum length of a sqid. pub fn min_length(self, value: u8) -> Self { - Self { min_length: Some(value), ..self } + Self { min_length: value, ..self } } /// Blocklist. When creating a sqid strings that begins @@ -199,7 +199,7 @@ impl SqidsBuilder { Ok(Sqids { alphabet: Sqids::shuffle(&alphabet), - min_length: self.min_length.unwrap_or(0), + min_length: self.min_length, blocklist: filtered_blocklist, }) } From 34e47df51320024aa75e91d278b7f877c1c14538 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 18 Jul 2025 22:28:38 +0200 Subject: [PATCH 4/5] Replace serde_json dependency with include! A JSON array of strings is valid as a Rust array literal, so parsing the JSON at runtime can be avoided entirely. --- Cargo.toml | 1 - src/lib.rs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e641e1f..75fdd32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,4 @@ readme = "README.md" keywords = ["ids", "encode", "short", "sqids", "hashids"] [dependencies] -serde_json = "1.0.134" thiserror = "2.0.9" diff --git a/src/lib.rs b/src/lib.rs index 8d6633d..360272c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,7 +69,8 @@ pub const DEFAULT_ALPHABET: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR /// Returns the default blocklist when none is given when creating a [Sqids]. pub fn default_blocklist() -> HashSet { - serde_json::from_str(include_str!("blocklist.json")).unwrap() + const DEFAULT_BLOCKLIST: &[&str] = &include!("blocklist.json"); + DEFAULT_BLOCKLIST.iter().map(|&s| s.to_owned()).collect() } /// Options for creating a [Sqids]. From 5db1b06d3522e2376dcf97d73bda347b5886b7ea Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 18 Jul 2025 22:31:05 +0200 Subject: [PATCH 5/5] Replace thiserror dependency with manual impls --- Cargo.toml | 1 - src/lib.rs | 29 +++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 75fdd32..6e8c53a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,3 @@ readme = "README.md" keywords = ["ids", "encode", "short", "sqids", "hashids"] [dependencies] -thiserror = "2.0.9" diff --git a/src/lib.rs b/src/lib.rs index 360272c..a5c566c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,12 +10,10 @@ /// **Note**: This is the crate's license and not an actual item. pub const LICENSE: () = (); -use std::{cmp::min, collections::HashSet, result}; - -use thiserror::Error; +use std::{cmp::min, collections::HashSet, fmt, result}; /// sqids Error type. -#[derive(Error, Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] pub enum Error { /// Alphabet cannot contain multibyte characters /// @@ -24,7 +22,6 @@ pub enum Error { /// let error = Sqids::builder().alphabet("☃️🦀🔥".chars().collect()).build().unwrap_err(); /// assert_eq!(error, Error::AlphabetMultibyteCharacters); /// ``` - #[error("Alphabet cannot contain multibyte characters")] AlphabetMultibyteCharacters, /// Alphabet length must be at least 3 /// @@ -33,7 +30,6 @@ pub enum Error { /// let error = Sqids::builder().alphabet("ab".chars().collect()).build().unwrap_err(); /// assert_eq!(error, Error::AlphabetLength); /// ``` - #[error("Alphabet length must be at least 3")] AlphabetLength, /// Alphabet must contain unique characters /// @@ -42,7 +38,6 @@ pub enum Error { /// let error = Sqids::builder().alphabet("aba".chars().collect()).build().unwrap_err(); /// assert_eq!(error, Error::AlphabetUniqueCharacters); /// ``` - #[error("Alphabet must contain unique characters")] AlphabetUniqueCharacters, /// Reached max attempts to re-generate the ID /// @@ -57,10 +52,28 @@ pub enum Error { /// let error = sqids.encode(&[1]).unwrap_err(); /// assert_eq!(error, Error::BlocklistMaxAttempts); /// ``` - #[error("Reached max attempts to re-generate the ID")] BlocklistMaxAttempts, } +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::AlphabetMultibyteCharacters => { + f.write_str("Alphabet cannot contain multibyte characters") + } + Error::AlphabetLength => f.write_str("Alphabet length must be at least 3"), + Error::AlphabetUniqueCharacters => { + f.write_str("Alphabet must contain unique characters") + } + Error::BlocklistMaxAttempts => { + f.write_str("Reached max attempts to re-generate the ID") + } + } + } +} + +impl std::error::Error for Error {} + /// type alias for Result pub type Result = result::Result;