#[cfg(feature = "circuit-template")]
use std::str::FromStr as _;
#[cfg(any(feature = "admin-service", feature = "benchmark"))]
use rand::{distributions::Alphanumeric, Rng};
#[cfg(any(feature = "admin-service", feature = "benchmark"))]
pub fn generate_random_base62_string(len: usize) -> String {
let mut rng = rand::thread_rng();
std::iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.map(char::from)
.take(len)
.collect()
}
#[cfg(feature = "circuit-template")]
pub fn next_base62_string(string: &str) -> Result<Option<String>, Base62Error> {
if string.is_empty() {
return Ok(None);
}
if !string.chars().all(|c| c.is_ascii_alphanumeric()) {
return Err(Base62Error::new("Invalid string: not base62"));
}
let (remaining_string, last_character) = string.split_at(string.len() - 1);
let last_character = char::from_str(last_character).expect("Failed to get char from str");
match next_base62_char(last_character) {
Some(next_char) => Ok(Some(remaining_string.to_owned() + &next_char.to_string())),
None => Ok(next_base62_string(remaining_string)?.map(|string| string + "0")),
}
}
#[cfg(feature = "circuit-template")]
pub fn next_base62_char(c: char) -> Option<char> {
let char_value = c as u32;
if char_value >= 'z' as u32 {
return None;
}
let next_char = std::char::from_u32(char_value + 1).expect("Failed to get char from u32");
if !next_char.is_ascii_alphanumeric() {
return next_base62_char(next_char);
}
Some(next_char)
}
#[cfg(feature = "circuit-template")]
#[derive(Debug)]
pub struct Base62Error(String);
#[cfg(feature = "circuit-template")]
impl Base62Error {
pub fn new(err: &str) -> Self {
Self(err.into())
}
}
#[cfg(feature = "circuit-template")]
impl std::error::Error for Base62Error {}
#[cfg(feature = "circuit-template")]
impl std::fmt::Display for Base62Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[cfg(any(feature = "admin-service", feature = "circuit-template"))]
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "admin-service")]
#[test]
fn random_string() {
let string1 = generate_random_base62_string(100);
assert_eq!(string1.len(), 100);
for c in string1.chars() {
assert!(c.is_ascii_alphanumeric());
}
let string2 = generate_random_base62_string(1000);
assert_eq!(string2.len(), 1000);
for c in string2.chars() {
assert!(c.is_ascii_alphanumeric());
}
}
#[cfg(feature = "circuit-template")]
#[test]
fn next_string() {
assert_eq!(
&next_base62_string("0000")
.expect("got err")
.expect("got none"),
"0001"
);
assert_eq!(
&next_base62_string("000z")
.expect("got err")
.expect("got none"),
"0010"
);
assert_eq!(
&next_base62_string("0010")
.expect("got err")
.expect("got none"),
"0011"
);
assert_eq!(
&next_base62_string("00zz")
.expect("got err")
.expect("got none"),
"0100"
);
assert_eq!(
&next_base62_string("0100")
.expect("got err")
.expect("got none"),
"0101"
);
assert_eq!(
&next_base62_string("0zzz")
.expect("got err")
.expect("got none"),
"1000"
);
assert_eq!(
&next_base62_string("1000")
.expect("got err")
.expect("got none"),
"1001"
);
assert!(next_base62_string("zzzz").unwrap().is_none());
assert!(next_base62_string("").unwrap().is_none());
assert!(next_base62_string("not_base62").is_err());
}
#[cfg(feature = "circuit-template")]
#[test]
fn next_char() {
assert_eq!(next_base62_char('0'), Some('1'));
assert_eq!(next_base62_char('9'), Some('A'));
assert_eq!(next_base62_char('A'), Some('B'));
assert_eq!(next_base62_char('Z'), Some('a'));
assert_eq!(next_base62_char('a'), Some('b'));
assert_eq!(next_base62_char('z'), None);
}
}