use crate::common::{Convertible, KEY_OBJ_SEPARATOR};
use crate::errors::{ErrorKind, NitriteError, NitriteResult};
use crate::repository::NitriteEntity;
pub fn get_key_name(name: &str) -> NitriteResult<String> {
if name.contains(KEY_OBJ_SEPARATOR) {
if let Some((_, key_part)) = name.split_once(KEY_OBJ_SEPARATOR) {
Ok(key_part.to_string())
} else {
log::error!("Invalid keyed object format: {}", name);
Err(NitriteError::new(
&format!("Invalid keyed object format: {}", name),
ErrorKind::ValidationError,
))
}
} else {
log::error!("{} is not a valid keyed object repository", name);
Err(NitriteError::new(
&format!("{} is not a valid keyed object repository", name),
ErrorKind::ValidationError,
))
}
}
pub fn get_keyed_repo_type(name: &str) -> NitriteResult<String> {
if name.contains(KEY_OBJ_SEPARATOR) {
if let Some((type_part, _)) = name.split_once(KEY_OBJ_SEPARATOR) {
Ok(type_part.to_string())
} else {
log::error!("Invalid keyed object format: {}", name);
Err(NitriteError::new(
&format!("Invalid keyed object format: {}", name),
ErrorKind::ValidationError,
))
}
} else {
log::error!("{} is not a valid keyed object repository", name);
Err(NitriteError::new(
&format!("{} is not a valid keyed object repository", name),
ErrorKind::ValidationError,
))
}
}
pub fn repository_name_by_type<T>(key: Option<&str>) -> NitriteResult<String>
where
T: NitriteEntity,
{
let entity_name = T::default().entity_name();
repository_name(&entity_name, key)
}
pub fn repository_name(entity_name: &str, key: Option<&str>) -> NitriteResult<String> {
if entity_name.contains(KEY_OBJ_SEPARATOR) {
log::error!("{} is not a valid entity name", entity_name);
return Err(NitriteError::new(
&format!("{} is not a valid entity name", entity_name),
ErrorKind::ValidationError,
));
}
match key {
Some(k) => {
let mut result = String::with_capacity(entity_name.len() + 1 + k.len());
result.push_str(entity_name);
result.push_str(KEY_OBJ_SEPARATOR);
result.push_str(k);
Ok(result)
}
None => Ok(entity_name.to_string()),
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_get_key_name_success() {
let result = get_key_name("User+key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "key");
}
#[test]
fn test_get_key_name_error() {
let result = get_key_name("User");
assert!(result.is_err());
}
#[test]
fn test_get_keyed_repo_type_success() {
let result = get_keyed_repo_type("User+key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "User");
}
#[test]
fn test_get_keyed_repo_type_error() {
let result = get_keyed_repo_type("User");
assert!(result.is_err());
}
#[test]
fn test_get_key_name_valid_standard_format() {
let result = get_key_name("Repository+EntityKey");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "EntityKey");
}
#[test]
fn test_get_key_name_with_separator_at_end() {
let result = get_key_name("Repository+");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "");
}
#[test]
fn test_get_key_name_with_multiple_separators() {
let result = get_key_name("Repo+Type+Key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Type+Key");
}
#[test]
fn test_get_key_name_without_separator() {
let result = get_key_name("RepositoryName");
assert!(result.is_err());
match result {
Err(e) => assert_eq!(*e.kind(), ErrorKind::ValidationError),
Ok(_) => panic!("Expected error for missing separator"),
}
}
#[test]
fn test_get_key_name_separator_at_start() {
let result = get_key_name("+Key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Key");
}
#[test]
fn test_get_keyed_repo_type_valid_standard_format() {
let result = get_keyed_repo_type("UserRepository+UserId");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "UserRepository");
}
#[test]
fn test_get_keyed_repo_type_with_separator_at_end() {
let result = get_keyed_repo_type("Repository+");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Repository");
}
#[test]
fn test_get_keyed_repo_type_with_multiple_separators() {
let result = get_keyed_repo_type("Repo+Type+Key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Repo");
}
#[test]
fn test_get_keyed_repo_type_without_separator() {
let result = get_keyed_repo_type("RepositoryName");
assert!(result.is_err());
match result {
Err(e) => assert_eq!(*e.kind(), ErrorKind::ValidationError),
Ok(_) => panic!("Expected error for missing separator"),
}
}
#[test]
fn test_get_keyed_repo_type_separator_at_start() {
let result = get_keyed_repo_type("+Key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "");
}
#[test]
fn test_get_key_name_no_bounds_panic_various_formats() {
let test_cases = vec![
("Repository+Key", true),
("+", true),
("Type+", true),
("+Value", true),
("NoSeparator", false),
];
for (input, should_succeed) in test_cases {
let result = get_key_name(input);
if should_succeed {
assert!(result.is_ok(), "Failed for input: {}", input);
} else {
assert!(result.is_err(), "Should error for input: {}", input);
}
}
}
#[test]
fn test_get_keyed_repo_type_no_bounds_panic_various_formats() {
let test_cases = vec![
("Repository+Key", true),
("+", true),
("Type+", true),
("+Value", true),
("NoSeparator", false),
];
for (input, should_succeed) in test_cases {
let result = get_keyed_repo_type(input);
if should_succeed {
assert!(result.is_ok(), "Failed for input: {}", input);
} else {
assert!(result.is_err(), "Should error for input: {}", input);
}
}
}
#[test]
fn bench_get_key_name() {
let start = std::time::Instant::now();
for _ in 0..10_000 {
let _ = get_key_name("Repository+EntityKey");
}
let elapsed = start.elapsed();
println!(
"get_key_name (10,000 calls): {:?} ({:.3}µs per call)",
elapsed,
elapsed.as_micros() as f64 / 10_000.0
);
}
#[test]
fn bench_get_keyed_repo_type() {
let start = std::time::Instant::now();
for _ in 0..10_000 {
let _ = get_keyed_repo_type("Repository+EntityKey");
}
let elapsed = start.elapsed();
println!(
"get_keyed_repo_type (10,000 calls): {:?} ({:.3}µs per call)",
elapsed,
elapsed.as_micros() as f64 / 10_000.0
);
}
#[test]
fn bench_find_repository_name_string_building() {
let start = std::time::Instant::now();
for _ in 0..10_000 {
let entity_name = "TestEntity";
let _result = String::with_capacity(entity_name.len() + 1 + 10);
}
let elapsed = start.elapsed();
println!(
"find_repository_name string capacity (10,000 calls): {:?} ({:.3}µs per call)",
elapsed,
elapsed.as_micros() as f64 / 10_000.0
);
}
}