use converter::BaseConverter;
use error::{CustomAlphabetError, ErrorKind, InvalidShortUuid};
pub mod converter;
mod error;
mod fmt;
mod macros;
use uuid;
pub const FLICKR_BASE_58: &str = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
pub const COOKIE_BASE_90: &str =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+-./:<=>?@[]^_`{|}~";
type UuidError = uuid::Error;
pub type Bytes = Vec<u8>;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct ShortUuid(Bytes);
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct ShortUuidCustom(Bytes);
pub type CustomAlphabet = &'static str;
pub struct CustomTranslator(BaseConverter);
impl CustomTranslator {
pub fn new(custom_alphabet: CustomAlphabet) -> Result<Self, CustomAlphabetError> {
let converter = BaseConverter::new_custom(custom_alphabet)?;
Ok(Self(converter))
}
fn as_slice(&self) -> &BaseConverter {
&self.0
}
}
impl From<ShortUuid> for ShortUuidCustom {
fn from(short_uuid: ShortUuid) -> Self {
ShortUuidCustom(short_uuid.0)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for ShortUuid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let string = String::from_utf8(self.0.clone())
.map_err(|e| serde::ser::Error::custom(e.to_string()))?;
serializer.serialize_str(&string)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for ShortUuid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
Ok(ShortUuid(string.into_bytes()))
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for ShortUuidCustom {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let string = String::from_utf8(self.0.clone())
.map_err(|e| serde::ser::Error::custom(e.to_string()))?;
serializer.serialize_str(&string)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for ShortUuidCustom {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
Ok(ShortUuidCustom(string.into_bytes()))
}
}
impl ShortUuid {
pub fn generate() -> ShortUuid {
generate_short(None)
}
pub fn from_uuid_str(uuid_string: &str) -> Result<ShortUuid, UuidError> {
let parsed = uuid::Uuid::parse_str(uuid_string)?;
let cleaned = parsed.to_string().to_lowercase().replace("-", "");
let converter = BaseConverter::default();
let result = converter.convert(&cleaned).unwrap();
Ok(ShortUuid(result))
}
pub fn from_uuid(uuid: &uuid::Uuid) -> ShortUuid {
let uuid_string = uuid.to_string();
let cleaned = uuid_string.to_lowercase().replace("-", "");
let converter = BaseConverter::default();
let result = converter.convert(&cleaned).unwrap();
ShortUuid(result)
}
pub fn to_uuid(self) -> uuid::Uuid {
let to_hex_converter = BaseConverter::default();
let result = to_hex_converter.convert_to_hex(&self.0).unwrap();
format_uuid(result)
}
pub fn parse_str(short_uuid_str: &str) -> Result<Self, InvalidShortUuid> {
let expected_len = 22;
if short_uuid_str.len() != expected_len {
return Err(InvalidShortUuid);
};
let byte_vector: Vec<u8> = short_uuid_str.as_bytes().to_vec();
let to_hex_converter = BaseConverter::default();
let result = to_hex_converter
.convert_to_hex(&byte_vector)
.map_err(|_| InvalidShortUuid)?;
uuid::Uuid::try_parse(&result).map_err(|_| InvalidShortUuid)?;
Ok(Self(byte_vector))
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
}
impl ShortUuidCustom {
pub fn generate(translator: &CustomTranslator) -> Self {
let generated = generate_short(Some(&translator.as_slice()));
let short_custom: ShortUuidCustom = generated.into();
short_custom
}
pub fn from_uuid(uuid: &uuid::Uuid, translator: &CustomTranslator) -> Self {
let uuid_string = uuid.to_string();
let cleaned = uuid_string.to_lowercase().replace("-", "");
let result = translator.as_slice().convert(&cleaned).unwrap();
Self(result)
}
pub fn from_uuid_str(
uuid_string: &str,
translator: &CustomTranslator,
) -> Result<Self, ErrorKind> {
let parsed = uuid::Uuid::parse_str(uuid_string).map_err(|e| ErrorKind::UuidError(e))?;
let cleaned = parsed.to_string().to_lowercase().replace("-", "");
let result = translator.as_slice().convert(&cleaned).unwrap();
Ok(Self(result))
}
pub fn to_uuid(self, translator: &CustomTranslator) -> Result<uuid::Uuid, CustomAlphabetError> {
let result = translator
.as_slice()
.convert_to_hex(&self.as_slice())
.unwrap();
let uuid_value = format_uuid(result);
Ok(uuid_value)
}
pub fn parse_str(
short_uuid_str: &str,
translator: &CustomTranslator,
) -> Result<Self, InvalidShortUuid> {
let byte_vector: Vec<u8> = short_uuid_str.as_bytes().to_vec();
let result_string = translator
.as_slice()
.convert_to_hex(&byte_vector)
.map_err(|_| InvalidShortUuid)?;
uuid::Uuid::try_parse(&result_string).map_err(|_| InvalidShortUuid)?;
Ok(Self(byte_vector))
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
}
fn generate_short(base_converter: Option<&BaseConverter>) -> ShortUuid {
let uuid_string = uuid::Uuid::new_v4().to_string();
let cleaned = uuid_string.to_lowercase().replace("-", "");
let result = base_converter
.unwrap_or(&BaseConverter::default())
.convert(&cleaned)
.unwrap();
ShortUuid(result)
}
fn format_uuid(value: String) -> uuid::Uuid {
let formatted_uuid = format!(
"{}-{}-{}-{}-{}",
&value[0..8],
&value[8..12],
&value[12..16],
&value[16..20],
&value[20..32]
);
let uuid = uuid::Uuid::parse_str(&formatted_uuid).unwrap();
return uuid;
}
impl From<uuid::Uuid> for ShortUuid {
fn from(uuid: uuid::Uuid) -> ShortUuid {
ShortUuid::from_uuid(&uuid)
}
}
impl From<ShortUuid> for uuid::Uuid {
fn from(short_uuid: ShortUuid) -> uuid::Uuid {
short_uuid.to_uuid()
}
}