mod errors;
mod magic_type_id;
mod magic_type_id_ext;
pub mod prelude {
pub use typeid_prefix::prelude::*;
pub use typeid_suffix::prelude::*;
pub use crate::errors::*;
pub use crate::magic_type_id::MagicTypeId;
pub use crate::magic_type_id_ext::MagicTypeIdExt;
#[cfg(feature = "instrument")]
pub use tracing;
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
use typeid_prefix::prelude::PrefixFactory;
use typeid_prefix::TypeIdPrefix;
use typeid_suffix::prelude::*;
use crate::errors::MagicTypeIdError;
use crate::magic_type_id::MagicTypeId;
use crate::magic_type_id_ext::MagicTypeIdExt;
#[test]
fn test_slug_id() {
let suffix = TypeIdSuffix::default();
let slug_id = MagicTypeId::new("prefix".create_prefix_sanitized(), suffix);
let slug_two: MagicTypeId = "another_prefix_01h455vb4pex5vsknk084sn02q".parse().unwrap();
let slug_bad: Result<MagicTypeId, MagicTypeIdError> =
"another_prefix_01h455vb4pNOPEsknk084sn02q".parse();
assert_eq!(slug_two.prefix().as_str(), "another_prefix");
assert_eq!(slug_id.prefix().to_string().as_str(), "prefix");
assert!(slug_bad.is_err());
}
#[test]
fn test_slug_id_ext() {
let slug_str = "prefix_01h455vb4pex5vsknk084sn02q";
assert_eq!(slug_str.prefix_str().unwrap(), "prefix");
assert_eq!(slug_str.prefix_str().unwrap().as_str(), "prefix");
assert_eq!(slug_str.uuid_str().unwrap().len(), 36);
let no_prefix = "01h455vb4pex5vsknk084sn02q";
assert_eq!(no_prefix.prefix_str().unwrap(), "");
assert!(no_prefix.prefix_str().unwrap().is_empty());
assert_eq!(no_prefix.uuid_str().unwrap().len(), 36);
let invalid_prefix = "Invalid_Prefix_01h455vb4pex5vsknk084sn02q";
assert_eq!(
invalid_prefix.create_prefix_sanitized().as_str(),
"invalid_prefix_hvbpexvsknksnq"
);
assert!(invalid_prefix.prefix_str().is_err());
assert_eq!(invalid_prefix.uuid_str().unwrap().len(), 36);
let invalid_suffix = "prefix_invalid";
assert_eq!(invalid_suffix.prefix_str().unwrap(), "prefix");
assert_eq!(invalid_suffix.prefix_str().unwrap().as_str(), "prefix");
assert!(invalid_suffix.suffix_str().is_err());
}
#[test]
fn test_nil() {
let slug_id: MagicTypeId = "00000000000000000000000000".parse().unwrap();
assert!(slug_id.prefix().is_empty());
assert_eq!(
slug_id.suffix().to_uuid().to_string(),
"00000000-0000-0000-0000-000000000000"
);
}
#[test]
fn test_new_creation_methods() {
let slug: MagicTypeId = "test_prefix".create_type_id::<V7>();
assert_eq!(slug.prefix().as_str(), "test_prefix");
assert_eq!(slug.suffix().to_string().len(), 26);
let slug_other: MagicTypeId = "other_prefix".create_type_id::<V3>();
assert_eq!(slug_other.prefix().as_str(), "other_prefix");
assert_eq!(slug_other.suffix().to_string().len(), 26);
let slug_try: Result<MagicTypeId, _> = "valid_prefix".try_create_type_id::<V7>();
assert!(slug_try.is_ok());
assert_eq!(slug_try.unwrap().prefix().as_str(), "valid_prefix");
let invalid_result: Result<MagicTypeId, _> = "Invalid Prefix".try_create_type_id::<V6>();
assert!(invalid_result.is_err());
}
#[test]
fn doc_smoke_tests() {
let sanitized_id = "Invalid Prefix!".create_type_id::<V7>();
assert!(sanitized_id.to_string().starts_with("invalidprefix_"));
let result = "Invalid Prefix!".try_create_type_id::<V7>();
assert!(result.is_err());
let id = "user".create_type_id::<V7>();
assert!(id.starts_with("user_"));
assert_eq!(id.len(), 31);
assert_eq!(id.as_str(), id.to_string());
let id_str = "product_01h455vb4pex5vsknk084sn02q";
let magic_id = MagicTypeId::from_str(id_str).unwrap();
assert_eq!(magic_id.prefix().as_str(), "product");
assert_eq!(magic_id.suffix().to_string(), "01h455vb4pex5vsknk084sn02q");
let uuid = magic_id.suffix().to_uuid();
println!("Extracted UUID: {uuid}");
let namespace = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap();
let name = "example.com";
let v5_uuid = Uuid::new_v5(&namespace, name.as_bytes());
let domain_id = MagicTypeId::new(
TypeIdPrefix::from_str("domain").unwrap(),
TypeIdSuffix::from(v5_uuid),
);
assert_eq!(
domain_id.uuid_str().unwrap(),
"cfbff0d1-9375-5685-968c-48ce8b15ae17"
);
}
#[test]
fn test_ordering() {
use std::thread::sleep;
use std::time::Duration;
let prefix1 = TypeIdPrefix::from_str("user").unwrap();
let prefix2 = TypeIdPrefix::from_str("admin").unwrap();
let id1 = MagicTypeId::new(prefix1.clone(), TypeIdSuffix::new::<V7>());
sleep(Duration::from_millis(10));
let id2 = MagicTypeId::new(prefix1, TypeIdSuffix::new::<V7>());
let id3 = MagicTypeId::new(
prefix2,
TypeIdSuffix::from_str(id2.suffix().as_ref()).unwrap(),
);
println!("id1: {id1}");
println!("id2: {id2}");
println!("id3: {id3}");
println!("Suffix id1: {}", id1.suffix());
println!("Suffix id2: {}", id2.suffix());
println!("Suffix id3: {}", id3.suffix());
let id1_vs_id2 = id1.cmp(&id2);
println!("id1 vs id2: {id1_vs_id2:?}");
let id3_vs_id1 = id3.cmp(&id1);
println!("id3 vs id1: {id3_vs_id1:?}");
let id3_vs_id2 = id3.cmp(&id2);
println!("id3 vs id2: {id3_vs_id2:?}");
assert!(
id1 < id2,
"Expected id1 to be less than id2 due to earlier timestamp"
);
assert_eq!(
id2.suffix(),
id3.suffix(),
"Suffixes for id2 and id3 should be the same"
);
assert!(id3 < id2, "Expected id3 to be less than id2 due to lexicographically smaller prefix when timestamps are equal");
}
#[test]
fn test_magictypeid_ordering() {
let prefix1 = TypeIdPrefix::from_str("user").unwrap();
let prefix2 = TypeIdPrefix::from_str("admin").unwrap();
let id1 = MagicTypeId::new(prefix1.clone(), TypeIdSuffix::new::<V7>());
sleep(Duration::from_millis(10)); let id2 = MagicTypeId::new(prefix1.clone(), TypeIdSuffix::new::<V7>());
let id3 = MagicTypeId::new(prefix2.clone(), TypeIdSuffix::new::<V7>());
println!("id1: {id1}");
println!("id2: {id2}");
println!("id3: {id3}");
assert!(
id1 < id2,
"Expected id1 to be less than id2 due to earlier timestamp"
);
let same_timestamp_suffix = id1.suffix().clone();
let id4 = MagicTypeId::new(prefix2, same_timestamp_suffix.clone());
let id5 = MagicTypeId::new(prefix1, same_timestamp_suffix);
assert!(id4 < id5, "Expected id4 (admin) to be less than id5 (user) due to lexicographically smaller prefix when timestamps are equal");
}
}