use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashSet;
use std::sync::Arc;
pub mod fields {
pub const ID: &str = "id";
pub const UUID: &str = "uuid";
pub const EMAIL: &str = "email";
pub const NAME: &str = "name";
pub const TITLE: &str = "title";
pub const DESCRIPTION: &str = "description";
pub const STATUS: &str = "status";
pub const ACTIVE: &str = "active";
pub const ENABLED: &str = "enabled";
pub const DELETED: &str = "deleted";
pub const CREATED_AT: &str = "created_at";
pub const UPDATED_AT: &str = "updated_at";
pub const DELETED_AT: &str = "deleted_at";
pub const USER_ID: &str = "user_id";
pub const AUTHOR_ID: &str = "author_id";
pub const PARENT_ID: &str = "parent_id";
pub const OWNER_ID: &str = "owner_id";
pub const TENANT_ID: &str = "tenant_id";
pub const ORG_ID: &str = "org_id";
pub const TYPE: &str = "type";
pub const KIND: &str = "kind";
pub const SLUG: &str = "slug";
pub const CONTENT: &str = "content";
pub const BODY: &str = "body";
pub const ORDER: &str = "order";
pub const POSITION: &str = "position";
pub const PRIORITY: &str = "priority";
pub const SCORE: &str = "score";
pub const COUNT: &str = "count";
pub const PRICE: &str = "price";
pub const AMOUNT: &str = "amount";
pub const QUANTITY: &str = "quantity";
pub const VERSION: &str = "version";
pub const AGE: &str = "age";
pub const ROLE: &str = "role";
pub const VERIFIED: &str = "verified";
pub const PASSWORD: &str = "password";
pub const FIRST_NAME: &str = "first_name";
pub const LAST_NAME: &str = "last_name";
pub const CATEGORY: &str = "category";
pub const TAGS: &str = "tags";
pub const PUBLISHED: &str = "published";
pub const PUBLISHED_AT: &str = "published_at";
pub const EXPIRES_AT: &str = "expires_at";
pub const STARTED_AT: &str = "started_at";
pub const COMPLETED_AT: &str = "completed_at";
pub const ARCHIVED: &str = "archived";
pub const FLAGGED: &str = "flagged";
pub const DATA: &str = "data";
pub const METADATA: &str = "metadata";
pub const URL: &str = "url";
pub const IMAGE_URL: &str = "image_url";
pub const AVATAR_URL: &str = "avatar_url";
pub const FILE: &str = "file";
pub const PATH: &str = "path";
pub const ATTEMPTS: &str = "attempts";
pub const MAX_ATTEMPTS: &str = "max_attempts";
pub const ALL_SORTED: &[&str] = &[
ACTIVE,
AGE,
AMOUNT,
ARCHIVED,
ATTEMPTS,
AUTHOR_ID,
AVATAR_URL,
BODY,
CATEGORY,
COMPLETED_AT,
CONTENT,
COUNT,
CREATED_AT,
DATA,
DELETED,
DELETED_AT,
DESCRIPTION,
EMAIL,
ENABLED,
EXPIRES_AT,
FILE,
FIRST_NAME,
FLAGGED,
ID,
IMAGE_URL,
KIND,
LAST_NAME,
MAX_ATTEMPTS,
METADATA,
NAME,
ORDER,
ORG_ID,
OWNER_ID,
PARENT_ID,
PASSWORD,
PATH,
POSITION,
PRICE,
PRIORITY,
PUBLISHED,
PUBLISHED_AT,
QUANTITY,
ROLE,
SCORE,
SLUG,
STARTED_AT,
STATUS,
TAGS,
TENANT_ID,
TITLE,
TYPE,
UPDATED_AT,
URL,
USER_ID,
UUID,
VERIFIED,
VERSION,
];
#[inline]
pub fn lookup(name: &str) -> Option<&'static str> {
ALL_SORTED.binary_search(&name).ok().map(|i| ALL_SORTED[i])
}
#[inline]
pub fn as_cow(name: &str) -> std::borrow::Cow<'static, str> {
match lookup(name) {
Some(s) => std::borrow::Cow::Borrowed(s),
None => std::borrow::Cow::Owned(name.to_string()),
}
}
}
thread_local! {
static INTERNER: RefCell<HashSet<Arc<str>>> = RefCell::new(HashSet::new());
}
#[inline]
pub fn intern(s: &str) -> Arc<str> {
INTERNER.with(|interner| {
let mut set = interner.borrow_mut();
if let Some(existing) = set.get(s) {
return Arc::clone(existing);
}
let arc: Arc<str> = Arc::from(s);
set.insert(Arc::clone(&arc));
arc
})
}
#[inline]
pub fn intern_cow(s: &str) -> Cow<'static, str> {
Cow::Owned(intern(s).to_string())
}
pub fn clear_interned() {
INTERNER.with(|interner| {
interner.borrow_mut().clear();
});
}
pub fn interned_count() -> usize {
INTERNER.with(|interner| interner.borrow().len())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_intern_returns_same_arc() {
clear_interned();
let s1 = intern("test_field");
let s2 = intern("test_field");
assert!(Arc::ptr_eq(&s1, &s2));
assert_eq!(&*s1, "test_field");
}
#[test]
fn test_intern_different_strings() {
clear_interned();
let s1 = intern("field_a");
let s2 = intern("field_b");
assert!(!Arc::ptr_eq(&s1, &s2));
assert_eq!(&*s1, "field_a");
assert_eq!(&*s2, "field_b");
}
#[test]
fn test_interned_count() {
clear_interned();
assert_eq!(interned_count(), 0);
intern("a");
assert_eq!(interned_count(), 1);
intern("b");
assert_eq!(interned_count(), 2);
intern("a"); assert_eq!(interned_count(), 2);
}
#[test]
fn test_clear_interned() {
clear_interned();
intern("x");
intern("y");
assert_eq!(interned_count(), 2);
clear_interned();
assert_eq!(interned_count(), 0);
}
#[test]
fn test_intern_cow() {
clear_interned();
let cow = intern_cow("field_name");
assert!(matches!(cow, Cow::Owned(_)));
assert_eq!(cow.as_ref(), "field_name");
}
#[test]
fn test_predefined_fields() {
assert_eq!(fields::ID, "id");
assert_eq!(fields::EMAIL, "email");
assert_eq!(fields::CREATED_AT, "created_at");
assert_eq!(fields::USER_ID, "user_id");
assert_eq!(fields::TENANT_ID, "tenant_id");
}
#[test]
fn test_intern_empty_string() {
clear_interned();
let s1 = intern("");
let s2 = intern("");
assert!(Arc::ptr_eq(&s1, &s2));
assert_eq!(&*s1, "");
}
#[test]
fn test_intern_unicode() {
clear_interned();
let s1 = intern("フィールド");
let s2 = intern("フィールド");
assert!(Arc::ptr_eq(&s1, &s2));
assert_eq!(&*s1, "フィールド");
}
#[test]
fn test_fields_lookup() {
assert_eq!(fields::lookup("id"), Some("id"));
assert_eq!(fields::lookup("email"), Some("email"));
assert_eq!(fields::lookup("created_at"), Some("created_at"));
assert_eq!(fields::lookup("user_id"), Some("user_id"));
assert_eq!(fields::lookup("status"), Some("status"));
assert_eq!(fields::lookup("unknown_field"), None);
assert_eq!(fields::lookup("custom_field_123"), None);
}
#[test]
fn test_fields_as_cow() {
let cow = fields::as_cow("id");
assert!(matches!(cow, Cow::Borrowed(_)));
assert_eq!(cow.as_ref(), "id");
let cow = fields::as_cow("custom_field");
assert!(matches!(cow, Cow::Owned(_)));
assert_eq!(cow.as_ref(), "custom_field");
}
#[test]
fn test_fields_all_sorted() {
let mut prev = "";
for &field in fields::ALL_SORTED {
assert!(
field >= prev,
"ALL_SORTED is not sorted: {} should come before {}",
prev,
field
);
prev = field;
}
}
}