Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,3 @@ readme = "README.md"
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"
66 changes: 49 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +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 derive_builder::Builder;
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
///
Expand All @@ -25,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
///
Expand All @@ -34,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
///
Expand All @@ -43,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
///
Expand All @@ -58,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<T, Error>
pub type Result<T> = result::Result<T, Error>;

Expand All @@ -70,7 +82,8 @@ pub const DEFAULT_ALPHABET: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR

/// Returns the default blocklist when none is given when creating a [Sqids].
pub fn default_blocklist() -> HashSet<String> {
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].
Expand Down Expand Up @@ -119,15 +132,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<char>,
/// 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<String>,
}

Expand All @@ -137,12 +145,36 @@ impl Default for Sqids {
}
}

/// Builder for [`Sqids`].
#[derive(Default)]
pub struct SqidsBuilder {
alphabet: Option<Vec<char>>,
min_length: u8,
blocklist: Option<HashSet<String>>,
}

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<char>) -> Self {
Self { alphabet: Some(value), ..self }
}

/// The minimum length of a sqid.
pub fn min_length(self, value: u8) -> Self {
Self { min_length: value, ..self }
}

/// Blocklist. When creating a sqid strings that begins
/// with one of these will be avoided.
pub fn blocklist(self, value: HashSet<String>) -> Self {
Self { blocklist: Some(value), ..self }
}

/// Build a [Sqids] object.
pub fn build(self) -> Result<Sqids> {
let alphabet: Vec<char> =
Expand Down Expand Up @@ -181,7 +213,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,
})
}
Expand Down