Skip to main content

domain_key/
key.rs

1//! Core Key implementation for domain-key
2//!
3//! This module contains the main `Key<T>` structure and its implementation,
4//! providing high-performance, type-safe key handling with extensive optimizations.
5
6use core::fmt;
7use core::hash::{Hash, Hasher};
8use core::marker::PhantomData;
9use core::str::FromStr;
10
11#[cfg(not(feature = "std"))]
12use alloc::borrow::Cow;
13#[cfg(not(feature = "std"))]
14use alloc::string::{String, ToString};
15
16#[cfg(feature = "std")]
17use std::borrow::Cow;
18
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Serialize};
21
22use smartstring::alias::String as SmartString;
23
24use crate::domain::KeyDomain;
25use crate::error::KeyParseError;
26use crate::utils;
27
28// ============================================================================
29// CONSTANTS
30// ============================================================================
31
32/// Default maximum allowed length for any key
33///
34/// This is a reasonable default that balances usability with performance.
35/// Keys up to this length can benefit from stack allocation optimizations.
36/// Domains can override this with their own limits.
37pub const DEFAULT_MAX_KEY_LENGTH: usize = 64;
38
39// ============================================================================
40// SPLIT ITERATOR TYPES
41// ============================================================================
42
43/// Split cache type for consistent API
44pub type SplitCache<'a> = core::str::Split<'a, char>;
45
46/// Split iterator with consistent API
47#[derive(Debug)]
48pub struct SplitIterator<'a>(SplitCache<'a>);
49
50impl<'a> Iterator for SplitIterator<'a> {
51    type Item = &'a str;
52
53    #[inline]
54    fn next(&mut self) -> Option<Self::Item> {
55        self.0.next()
56    }
57}
58
59// ============================================================================
60// CORE KEY IMPLEMENTATION
61// ============================================================================
62
63/// High-performance generic key type with advanced optimizations
64///
65/// This is the core key type that provides type safety through the domain
66/// marker `T`. Keys are immutable after creation and use `SmartString` for
67/// optimal memory usage (stack allocation for short keys, heap for longer ones).
68///
69/// # Performance Characteristics
70///
71/// - **Memory Layout**: 32 bytes total (fits in single cache line)
72/// - **Hash Access**: O(1) via pre-computed hash
73/// - **Length Access**: O(1) via cached length field
74/// - **String Access**: Direct reference to internal storage
75/// - **Clone**: Efficient via `SmartString`'s copy-on-write semantics
76///
77/// # Type Parameters
78///
79/// * `T` - A domain marker type that implements `KeyDomain`
80///
81/// # Memory Layout
82///
83/// ```text
84/// Key<T> struct (32 bytes, cache-line friendly):
85/// ┌─────────────────────┬──────────┬─────────┬─────────────┐
86/// │ SmartString (24B)   │ hash (8B)│ len (4B)│ marker (0B) │
87/// └─────────────────────┴──────────┴─────────┴─────────────┘
88/// ```
89///
90/// Keys use `SmartString` which stores strings up to 23 bytes inline on the stack,
91/// only allocating on the heap for longer strings. Additionally, the pre-computed
92/// hash is stored for O(1) hash operations.
93///
94/// # Examples
95///
96/// ```rust
97/// use domain_key::{Key, Domain, KeyDomain};
98///
99/// #[derive(Debug)]
100/// struct UserDomain;
101///
102/// impl Domain for UserDomain {
103///     const DOMAIN_NAME: &'static str = "user";
104/// }
105///
106/// impl KeyDomain for UserDomain {
107///     const MAX_LENGTH: usize = 32;
108/// }
109///
110/// type UserKey = Key<UserDomain>;
111///
112/// let key = UserKey::new("john_doe")?;
113/// assert_eq!(key.as_str(), "john_doe");
114/// assert_eq!(key.domain(), "user");
115/// assert_eq!(key.len(), 8);
116/// # Ok::<(), domain_key::KeyParseError>(())
117/// ```
118#[derive(Debug)]
119pub struct Key<T: KeyDomain> {
120    /// Internal string storage using `SmartString` for optimal memory usage
121    inner: SmartString,
122
123    /// Pre-computed hash value for O(1) hash operations
124    ///
125    /// This hash is computed once during key creation and cached for the
126    /// lifetime of the key, providing significant performance benefits
127    /// for hash-based collections.
128    hash: u64,
129
130    /// Cached length for O(1) length access
131    ///
132    /// This optimization eliminates the need to traverse the string to
133    /// determine its length, which can be a significant performance
134    /// improvement in hot paths.
135    length: u32,
136
137    /// Zero-sized type marker for compile-time type safety
138    ///
139    /// This field provides compile-time type safety without any runtime
140    /// overhead. Different domain types cannot be mixed or compared.
141    _marker: PhantomData<T>,
142}
143
144// Manual Clone implementation to ensure optimal performance
145impl<T: KeyDomain> Clone for Key<T> {
146    /// Efficient clone implementation
147    ///
148    /// Cloning a key is efficient due to `SmartString`'s optimizations:
149    /// - For inline strings (≤23 chars): Simple memory copy
150    /// - For heap strings: Reference counting or copy-on-write
151    #[inline]
152    fn clone(&self) -> Self {
153        Self {
154            inner: self.inner.clone(),
155            hash: self.hash,
156            length: self.length,
157            _marker: PhantomData,
158        }
159    }
160}
161
162// Manual PartialEq/Eq — compare only the key string, not cached fields
163impl<T: KeyDomain> PartialEq for Key<T> {
164    #[inline]
165    fn eq(&self, other: &Self) -> bool {
166        self.inner == other.inner
167    }
168}
169
170impl<T: KeyDomain> Eq for Key<T> {}
171
172// Manual PartialOrd/Ord — compare only the key string
173impl<T: KeyDomain> PartialOrd for Key<T> {
174    #[inline]
175    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
176        Some(self.cmp(other))
177    }
178}
179
180impl<T: KeyDomain> Ord for Key<T> {
181    #[inline]
182    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
183        self.inner.cmp(&other.inner)
184    }
185}
186
187// Manual Hash implementation using cached hash for maximum performance
188impl<T: KeyDomain> Hash for Key<T> {
189    /// O(1) hash implementation using pre-computed hash
190    ///
191    /// This is significantly faster than re-hashing the string content
192    /// every time the key is used in hash-based collections.
193    #[inline]
194    fn hash<H: Hasher>(&self, state: &mut H) {
195        state.write_u64(self.hash);
196    }
197}
198
199// Conditional Serde support for serialization/deserialization
200#[cfg(feature = "serde")]
201impl<T: KeyDomain> Serialize for Key<T> {
202    /// Serialize the key as its string representation
203    ///
204    /// Keys are serialized as their string content, not including
205    /// the cached hash or length for efficiency and compatibility.
206    #[inline]
207    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208    where
209        S: serde::Serializer,
210    {
211        self.inner.serialize(serializer)
212    }
213}
214
215#[cfg(feature = "serde")]
216impl<'de, T: KeyDomain> Deserialize<'de> for Key<T> {
217    /// Deserialize and validate a key from its string representation
218    ///
219    /// This implementation chooses the optimal deserialization strategy
220    /// based on the format (human-readable vs binary) for best performance.
221    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222    where
223        D: serde::Deserializer<'de>,
224    {
225        if deserializer.is_human_readable() {
226            // For human-readable formats (JSON, YAML), use zero-copy when possible
227            let s = <&str>::deserialize(deserializer)?;
228            Key::new(s).map_err(serde::de::Error::custom)
229        } else {
230            // For binary formats, deserialize as owned string
231            let s = String::deserialize(deserializer)?;
232            Key::from_string(s).map_err(serde::de::Error::custom)
233        }
234    }
235}
236
237// ============================================================================
238// KEY IMPLEMENTATION - CORE METHODS
239// ============================================================================
240
241impl<T: KeyDomain> Key<T> {
242    /// Creates a new key with comprehensive validation and optimization
243    ///
244    /// This method performs both common validation (length, characters) and
245    /// domain-specific validation according to the key's domain type. It
246    /// automatically chooses the optimal creation path based on the input
247    /// characteristics and domain configuration.
248    ///
249    /// # Arguments
250    ///
251    /// * `key` - String-like input that will be normalized and validated
252    ///
253    /// # Returns
254    ///
255    /// * `Ok(Key<T>)` if the key is valid
256    /// * `Err(KeyParseError)` with the specific validation failure
257    ///
258    /// # Errors
259    ///
260    /// Returns `KeyParseError` if the key fails common validation (empty, too
261    /// long, invalid characters) or domain-specific validation rules
262    ///
263    /// # Examples
264    ///
265    /// ```rust
266    /// use domain_key::{Key, Domain, KeyDomain};
267    ///
268    /// #[derive(Debug)]
269    /// struct TestDomain;
270    /// impl Domain for TestDomain {
271    ///     const DOMAIN_NAME: &'static str = "test";
272    /// }
273    /// impl KeyDomain for TestDomain {}
274    /// type TestKey = Key<TestDomain>;
275    ///
276    /// let key = TestKey::new("valid_key")?;
277    /// assert_eq!(key.as_str(), "valid_key");
278    ///
279    /// // Invalid keys return descriptive errors
280    /// let error = TestKey::new("").unwrap_err();
281    /// assert!(matches!(error, domain_key::KeyParseError::Empty));
282    /// # Ok::<(), domain_key::KeyParseError>(())
283    /// ```
284    #[inline]
285    pub fn new(key: impl AsRef<str>) -> Result<Self, KeyParseError> {
286        let key_str = key.as_ref();
287        Self::new_optimized(key_str)
288    }
289
290    /// Optimized implementation for key creation
291    ///
292    /// This method uses performance optimizations when available:
293    /// - Stack allocation for short keys
294    /// - Fast validation paths
295    /// - Cached operations
296    ///
297    /// # Errors
298    ///
299    /// Returns `KeyParseError` if the constructed key fails validation
300    fn new_optimized(key: &str) -> Result<Self, KeyParseError> {
301        // Step 1: Reject obviously empty input before normalization
302        if key.trim().is_empty() {
303            return Err(KeyParseError::Empty);
304        }
305
306        // Step 2: Normalization (trimming, lowercasing, domain-specific)
307        let normalized = Self::normalize(key);
308
309        // Step 3: Common validation on the normalized result
310        Self::validate_common(&normalized)?;
311
312        // Step 4: Domain-specific validation
313        T::validate_domain_rules(&normalized).map_err(Self::fix_domain_error)?;
314
315        // Step 4: Hash computation and storage
316        let hash = Self::compute_hash(&normalized);
317        let length = u32::try_from(normalized.len()).map_err(|_| KeyParseError::TooLong {
318            max_length: u32::MAX as usize,
319            actual_length: normalized.len(),
320        })?;
321
322        Ok(Self {
323            inner: SmartString::from(normalized.as_ref()),
324            hash,
325            length,
326            _marker: PhantomData,
327        })
328    }
329
330    /// Creates a new key from an owned String with optimized handling
331    ///
332    /// This method is more efficient when you already have a `String` as it
333    /// can reuse the allocation when possible.
334    ///
335    /// # Arguments
336    ///
337    /// * `key` - Owned string that will be normalized and validated
338    ///
339    /// # Errors
340    ///
341    /// Returns `KeyParseError` if the key fails validation
342    ///
343    /// # Examples
344    ///
345    /// ```rust
346    /// use domain_key::{Key, Domain, KeyDomain};
347    ///
348    /// #[derive(Debug)]
349    /// struct TestDomain;
350    /// impl Domain for TestDomain {
351    ///     const DOMAIN_NAME: &'static str = "test";
352    /// }
353    /// impl KeyDomain for TestDomain {}
354    /// type TestKey = Key<TestDomain>;
355    ///
356    /// let key_string = "test_key".to_string();
357    /// let key = TestKey::from_string(key_string)?;
358    /// assert_eq!(key.as_str(), "test_key");
359    /// # Ok::<(), domain_key::KeyParseError>(())
360    /// ```
361    pub fn from_string(key: String) -> Result<Self, KeyParseError> {
362        // Reject obviously empty input before normalization
363        if key.trim().is_empty() {
364            return Err(KeyParseError::Empty);
365        }
366
367        // Normalize efficiently, reusing allocation when possible
368        let normalized = Self::normalize_owned(key);
369
370        // Validate the normalized result
371        Self::validate_common(&normalized)?;
372
373        // Domain validation
374        T::validate_domain_rules(&normalized).map_err(Self::fix_domain_error)?;
375
376        let hash = Self::compute_hash(&normalized);
377        let length = u32::try_from(normalized.len()).map_err(|_| KeyParseError::TooLong {
378            max_length: u32::MAX as usize,
379            actual_length: normalized.len(),
380        })?;
381
382        Ok(Self {
383            inner: SmartString::from(normalized),
384            hash,
385            length,
386            _marker: PhantomData,
387        })
388    }
389
390    /// Create a key from multiple parts separated by a delimiter
391    ///
392    /// This method efficiently constructs a key from multiple string parts,
393    /// using pre-calculated sizing to minimize allocations.
394    ///
395    /// # Arguments
396    ///
397    /// * `parts` - Array of string parts to join
398    /// * `delimiter` - String to use as separator between parts
399    ///
400    /// # Returns
401    ///
402    /// * `Ok(Key<T>)` if the constructed key is valid
403    /// * `Err(KeyParseError)` if validation fails
404    ///
405    /// # Examples
406    ///
407    /// ```rust
408    /// use domain_key::{Key, Domain, KeyDomain};
409    ///
410    /// #[derive(Debug)]
411    /// struct TestDomain;
412    /// impl Domain for TestDomain {
413    ///     const DOMAIN_NAME: &'static str = "test";
414    /// }
415    /// impl KeyDomain for TestDomain {}
416    /// type TestKey = Key<TestDomain>;
417    ///
418    /// let key = TestKey::from_parts(&["user", "123", "profile"], "_")?;
419    /// assert_eq!(key.as_str(), "user_123_profile");
420    /// # Ok::<(), domain_key::KeyParseError>(())
421    /// ```
422    /// # Errors
423    ///
424    /// Returns `KeyParseError` if the constructed key fails validation
425    pub fn from_parts(parts: &[&str], delimiter: &str) -> Result<Self, KeyParseError> {
426        if parts.is_empty() {
427            return Err(KeyParseError::Empty);
428        }
429
430        if parts.iter().any(|part| part.is_empty()) {
431            return Err(KeyParseError::InvalidStructure {
432                reason: "Parts cannot contain empty strings",
433            });
434        }
435
436        let joined = parts.join(delimiter);
437
438        if joined.is_empty() {
439            return Err(KeyParseError::Empty);
440        }
441
442        Self::from_string(joined)
443    }
444
445    /// Try to create a key from multiple parts, returning None on failure
446    ///
447    /// This is a convenience method for when you want to handle validation
448    /// failures by ignoring invalid keys rather than handling errors.
449    ///
450    /// # Examples
451    ///
452    /// ```rust
453    /// use domain_key::{Key, Domain, KeyDomain};
454    ///
455    /// #[derive(Debug)]
456    /// struct TestDomain;
457    /// impl Domain for TestDomain {
458    ///     const DOMAIN_NAME: &'static str = "test";
459    /// }
460    /// impl KeyDomain for TestDomain {}
461    /// type TestKey = Key<TestDomain>;
462    ///
463    /// let valid = TestKey::try_from_parts(&["user", "123"], "_").unwrap();
464    /// let invalid = TestKey::try_from_parts(&["", ""], "_"); // Returns None
465    /// assert!(invalid.is_none());
466    /// ```
467    #[must_use]
468    pub fn try_from_parts(parts: &[&str], delimiter: &str) -> Option<Self> {
469        Self::from_parts(parts, delimiter).ok()
470    }
471
472    /// Creates a key from a static string without runtime validation
473    ///
474    /// # Warning
475    ///
476    /// The caller must ensure that the static string follows all validation
477    /// rules for the domain (allowed characters, length limits, normalization,
478    /// domain-specific rules). Invalid keys created this way will violate
479    /// internal invariants and may cause unexpected behavior.
480    ///
481    /// Prefer [`try_from_static`](Self::try_from_static) or the [`static_key!`](macro@crate::static_key) macro
482    /// for safe creation of static keys.
483    ///
484    /// # Panics
485    ///
486    /// Panics in debug builds if the key is empty or exceeds `u32::MAX` length.
487    ///
488    /// # Arguments
489    ///
490    /// * `key` - A static string literal that represents a valid key
491    ///
492    /// # Examples
493    ///
494    /// ```rust
495    /// use domain_key::{Key, Domain, KeyDomain};
496    ///
497    /// #[derive(Debug)]
498    /// struct TestDomain;
499    /// impl Domain for TestDomain {
500    ///     const DOMAIN_NAME: &'static str = "test";
501    /// }
502    /// impl KeyDomain for TestDomain {}
503    /// type TestKey = Key<TestDomain>;
504    ///
505    /// let key = TestKey::from_static_unchecked("static_key");
506    /// assert_eq!(key.as_str(), "static_key");
507    /// ```
508    #[must_use]
509    pub fn from_static_unchecked(key: &'static str) -> Self {
510        debug_assert!(
511            !key.is_empty(),
512            "from_static_unchecked: key must not be empty"
513        );
514        debug_assert!(
515            key.len() <= T::MAX_LENGTH,
516            "from_static_unchecked: key length {} exceeds domain max {}",
517            key.len(),
518            T::MAX_LENGTH
519        );
520
521        let hash = Self::compute_hash(key);
522        #[allow(clippy::cast_possible_truncation)]
523        let length = key.len() as u32;
524
525        Self {
526            inner: SmartString::from(key),
527            hash,
528            length,
529            _marker: PhantomData,
530        }
531    }
532
533    /// Creates a key from a static string with validation
534    ///
535    /// This is a safer alternative to `from_static_unchecked` that validates
536    /// the key at runtime. The validation cost is paid once, and subsequent
537    /// uses of the key are as fast as the unchecked version.
538    ///
539    /// # Arguments
540    ///
541    /// * `key` - A static string literal to validate and convert
542    ///
543    /// # Errors
544    ///
545    /// Returns `KeyParseError` if the static key fails validation
546    ///
547    /// # Examples
548    ///
549    /// ```rust
550    /// use domain_key::{Key, Domain, KeyDomain};
551    ///
552    /// #[derive(Debug)]
553    /// struct TestDomain;
554    /// impl Domain for TestDomain {
555    ///     const DOMAIN_NAME: &'static str = "test";
556    /// }
557    /// impl KeyDomain for TestDomain {}
558    /// type TestKey = Key<TestDomain>;
559    ///
560    /// let key = TestKey::try_from_static("static_key")?;
561    /// assert_eq!(key.as_str(), "static_key");
562    ///
563    /// let invalid = TestKey::try_from_static("");
564    /// assert!(invalid.is_err());
565    /// # Ok::<(), domain_key::KeyParseError>(())
566    /// ```
567    /// # Errors
568    ///
569    /// Returns `KeyParseError` if the constructed key fails validation
570    pub fn try_from_static(key: &'static str) -> Result<Self, KeyParseError> {
571        // Validate and create via the normal path
572        Self::new(key)
573    }
574
575    /// Try to create a key, returning None on validation failure
576    ///
577    /// This is a convenience method for when you want to handle validation
578    /// failures by ignoring invalid keys rather than handling errors.
579    ///
580    /// # Examples
581    ///
582    /// ```rust
583    /// use domain_key::{Key, Domain, KeyDomain};
584    ///
585    /// #[derive(Debug)]
586    /// struct TestDomain;
587    /// impl Domain for TestDomain {
588    ///     const DOMAIN_NAME: &'static str = "test";
589    /// }
590    /// impl KeyDomain for TestDomain {}
591    /// type TestKey = Key<TestDomain>;
592    ///
593    /// let valid = TestKey::try_new("valid_key").unwrap();
594    /// let invalid = TestKey::try_new(""); // Returns None
595    /// assert!(invalid.is_none());
596    /// ```
597    #[inline]
598    pub fn try_new(key: impl AsRef<str>) -> Option<Self> {
599        Self::new(key).ok()
600    }
601}
602
603// ============================================================================
604// KEY IMPLEMENTATION - ACCESSOR METHODS
605// ============================================================================
606
607impl<T: KeyDomain> Key<T> {
608    /// Returns the key as a string slice
609    ///
610    /// This is the primary way to access the string content of a key.
611    /// The returned reference is valid for the lifetime of the key.
612    ///
613    /// # Examples
614    ///
615    /// ```rust
616    /// use domain_key::{Key, Domain, KeyDomain};
617    ///
618    /// #[derive(Debug)]
619    /// struct TestDomain;
620    /// impl Domain for TestDomain {
621    ///     const DOMAIN_NAME: &'static str = "test";
622    /// }
623    /// impl KeyDomain for TestDomain {}
624    /// type TestKey = Key<TestDomain>;
625    ///
626    /// let key = TestKey::new("example")?;
627    /// assert_eq!(key.as_str(), "example");
628    /// # Ok::<(), domain_key::KeyParseError>(())
629    /// ```
630    #[inline]
631    #[must_use]
632    pub fn as_str(&self) -> &str {
633        &self.inner
634    }
635
636    /// Returns the domain name for this key type
637    ///
638    /// This is a compile-time constant that identifies which domain
639    /// this key belongs to.
640    ///
641    /// # Examples
642    ///
643    /// ```rust
644    /// use domain_key::{Key, Domain, KeyDomain};
645    ///
646    /// #[derive(Debug)]
647    /// struct UserDomain;
648    /// impl Domain for UserDomain {
649    ///     const DOMAIN_NAME: &'static str = "user";
650    /// }
651    /// impl KeyDomain for UserDomain {}
652    /// type UserKey = Key<UserDomain>;
653    ///
654    /// let key = UserKey::new("john")?;
655    /// assert_eq!(key.domain(), "user");
656    /// # Ok::<(), domain_key::KeyParseError>(())
657    /// ```
658    #[inline]
659    #[must_use]
660    pub const fn domain(&self) -> &'static str {
661        T::DOMAIN_NAME
662    }
663
664    /// Returns the length of the key string
665    ///
666    /// This is an O(1) operation using a cached length value.
667    ///
668    /// # Examples
669    ///
670    /// ```rust
671    /// use domain_key::{Key, Domain, KeyDomain};
672    ///
673    /// #[derive(Debug)]
674    /// struct TestDomain;
675    /// impl Domain for TestDomain {
676    ///     const DOMAIN_NAME: &'static str = "test";
677    /// }
678    /// impl KeyDomain for TestDomain {}
679    /// type TestKey = Key<TestDomain>;
680    ///
681    /// let key = TestKey::new("example")?;
682    /// assert_eq!(key.len(), 7);
683    /// # Ok::<(), domain_key::KeyParseError>(())
684    /// ```
685    #[inline]
686    #[must_use]
687    pub fn len(&self) -> usize {
688        self.length as usize
689    }
690
691    /// Returns true if the key is empty (this should never happen for valid keys)
692    ///
693    /// Since empty keys are rejected during validation, this method should
694    /// always return `false` for properly constructed keys. It's provided
695    /// for completeness and debugging purposes.
696    ///
697    /// # Examples
698    ///
699    /// ```rust
700    /// use domain_key::{Key, Domain, KeyDomain};
701    ///
702    /// #[derive(Debug)]
703    /// struct TestDomain;
704    /// impl Domain for TestDomain {
705    ///     const DOMAIN_NAME: &'static str = "test";
706    /// }
707    /// impl KeyDomain for TestDomain {}
708    /// type TestKey = Key<TestDomain>;
709    ///
710    /// let key = TestKey::new("example")?;
711    /// assert!(!key.is_empty());
712    /// # Ok::<(), domain_key::KeyParseError>(())
713    /// ```
714    #[inline]
715    #[must_use]
716    pub fn is_empty(&self) -> bool {
717        self.length == 0
718    }
719
720    /// Returns the cached hash value
721    ///
722    /// This hash is computed once during key creation and cached for the
723    /// lifetime of the key. It's used internally for hash-based collections
724    /// and can be useful for custom hash-based data structures.
725    ///
726    /// **Note:** The hash algorithm depends on the active feature flags
727    /// (`fast`, `secure`, `crypto`, or the default hasher). Keys created
728    /// with different feature configurations will produce different hash
729    /// values. Do not persist or compare hash values across builds with
730    /// different features.
731    ///
732    /// # Examples
733    ///
734    /// ```rust
735    /// use domain_key::{Key, Domain, KeyDomain};
736    ///
737    /// #[derive(Debug)]
738    /// struct TestDomain;
739    /// impl Domain for TestDomain {
740    ///     const DOMAIN_NAME: &'static str = "test";
741    /// }
742    /// impl KeyDomain for TestDomain {}
743    /// type TestKey = Key<TestDomain>;
744    ///
745    /// let key1 = TestKey::new("example")?;
746    /// let key2 = TestKey::new("example")?;
747    /// let key3 = TestKey::new("different")?;
748    ///
749    /// // Same keys have same hash
750    /// assert_eq!(key1.hash(), key2.hash());
751    /// // Different keys have different hashes (with high probability)
752    /// assert_ne!(key1.hash(), key3.hash());
753    /// # Ok::<(), domain_key::KeyParseError>(())
754    /// ```
755    #[inline]
756    #[must_use]
757    pub const fn hash(&self) -> u64 {
758        self.hash
759    }
760
761    /// Checks if this key starts with the given prefix
762    ///
763    /// This is a simple string prefix check that can be useful for
764    /// categorizing or filtering keys.
765    ///
766    /// # Arguments
767    ///
768    /// * `prefix` - The prefix string to check for
769    ///
770    /// # Examples
771    ///
772    /// ```rust
773    /// use domain_key::{Key, Domain, KeyDomain};
774    ///
775    /// #[derive(Debug)]
776    /// struct TestDomain;
777    /// impl Domain for TestDomain {
778    ///     const DOMAIN_NAME: &'static str = "test";
779    /// }
780    /// impl KeyDomain for TestDomain {}
781    /// type TestKey = Key<TestDomain>;
782    ///
783    /// let key = TestKey::new("user_profile")?;
784    /// assert!(key.starts_with("user_"));
785    /// assert!(!key.starts_with("admin_"));
786    /// # Ok::<(), domain_key::KeyParseError>(())
787    /// ```
788    #[inline]
789    #[must_use]
790    pub fn starts_with(&self, prefix: &str) -> bool {
791        self.inner.starts_with(prefix)
792    }
793
794    /// Checks if this key ends with the given suffix
795    ///
796    /// This is a simple string suffix check that can be useful for
797    /// categorizing or filtering keys.
798    ///
799    /// # Arguments
800    ///
801    /// * `suffix` - The suffix string to check for
802    ///
803    /// # Examples
804    ///
805    /// ```rust
806    /// use domain_key::{Key, Domain, KeyDomain};
807    ///
808    /// #[derive(Debug)]
809    /// struct TestDomain;
810    /// impl Domain for TestDomain {
811    ///     const DOMAIN_NAME: &'static str = "test";
812    /// }
813    /// impl KeyDomain for TestDomain {}
814    /// type TestKey = Key<TestDomain>;
815    ///
816    /// let key = TestKey::new("user_profile")?;
817    /// assert!(key.ends_with("_profile"));
818    /// assert!(!key.ends_with("_settings"));
819    /// # Ok::<(), domain_key::KeyParseError>(())
820    /// ```
821    #[inline]
822    #[must_use]
823    pub fn ends_with(&self, suffix: &str) -> bool {
824        self.inner.ends_with(suffix)
825    }
826
827    /// Checks if this key contains the given substring
828    ///
829    /// This performs a substring search within the key.
830    ///
831    /// # Arguments
832    ///
833    /// * `pattern` - The substring to search for
834    ///
835    /// # Examples
836    ///
837    /// ```rust
838    /// use domain_key::{Key, Domain, KeyDomain};
839    ///
840    /// #[derive(Debug)]
841    /// struct TestDomain;
842    /// impl Domain for TestDomain {
843    ///     const DOMAIN_NAME: &'static str = "test";
844    /// }
845    /// impl KeyDomain for TestDomain {}
846    /// type TestKey = Key<TestDomain>;
847    ///
848    /// let key = TestKey::new("user_profile_settings")?;
849    /// assert!(key.contains("profile"));
850    /// assert!(!key.contains("admin"));
851    /// # Ok::<(), domain_key::KeyParseError>(())
852    /// ```
853    #[inline]
854    #[must_use]
855    pub fn contains(&self, pattern: &str) -> bool {
856        self.inner.contains(pattern)
857    }
858
859    /// Returns an iterator over the characters of the key
860    ///
861    /// This provides access to individual characters in the key string.
862    ///
863    /// # Examples
864    ///
865    /// ```rust
866    /// use domain_key::{Key, Domain, KeyDomain};
867    ///
868    /// #[derive(Debug)]
869    /// struct TestDomain;
870    /// impl Domain for TestDomain {
871    ///     const DOMAIN_NAME: &'static str = "test";
872    /// }
873    /// impl KeyDomain for TestDomain {}
874    /// type TestKey = Key<TestDomain>;
875    ///
876    /// let key = TestKey::new("abc")?;
877    /// let chars: Vec<char> = key.chars().collect();
878    /// assert_eq!(chars, vec!['a', 'b', 'c']);
879    /// # Ok::<(), domain_key::KeyParseError>(())
880    /// ```
881    #[inline]
882    pub fn chars(&self) -> core::str::Chars<'_> {
883        self.inner.chars()
884    }
885
886    /// Splits the key by a delimiter and returns an iterator
887    ///
888    /// This method provides consistent split functionality.
889    ///
890    /// # Arguments
891    ///
892    /// * `delimiter` - Character to split on
893    ///
894    /// # Examples
895    ///
896    /// ```rust
897    /// use domain_key::{Key, Domain, KeyDomain};
898    ///
899    /// #[derive(Debug)]
900    /// struct TestDomain;
901    /// impl Domain for TestDomain {
902    ///     const DOMAIN_NAME: &'static str = "test";
903    /// }
904    /// impl KeyDomain for TestDomain {}
905    /// type TestKey = Key<TestDomain>;
906    ///
907    /// let key = TestKey::new("user_profile_settings")?;
908    /// let parts: Vec<&str> = key.split('_').collect();
909    /// assert_eq!(parts, vec!["user", "profile", "settings"]);
910    /// # Ok::<(), domain_key::KeyParseError>(())
911    /// ```
912    #[must_use]
913    pub fn split(&self, delimiter: char) -> SplitIterator<'_> {
914        SplitIterator(utils::new_split_cache(&self.inner, delimiter))
915    }
916
917    /// Split operation for consistent API
918    ///
919    /// This method provides the same functionality as `split()` but with explicit naming
920    /// for cases where caching behavior needs to be clear.
921    #[must_use]
922    pub fn split_cached(&self, delimiter: char) -> SplitCache<'_> {
923        utils::new_split_cache(&self.inner, delimiter)
924    }
925
926    /// Splits the key by a string delimiter and returns an iterator
927    ///
928    /// This method splits the key using a string pattern rather than a single character.
929    ///
930    /// # Examples
931    ///
932    /// ```rust
933    /// use domain_key::{Key, Domain, KeyDomain};
934    ///
935    /// #[derive(Debug)]
936    /// struct TestDomain;
937    /// impl Domain for TestDomain {
938    ///     const DOMAIN_NAME: &'static str = "test";
939    /// }
940    /// impl KeyDomain for TestDomain {}
941    /// type TestKey = Key<TestDomain>;
942    ///
943    /// let key = TestKey::new("user-and-profile-and-settings")?;
944    /// let parts: Vec<&str> = key.split_str("-and-").collect();
945    /// assert_eq!(parts, vec!["user", "profile", "settings"]);
946    /// # Ok::<(), domain_key::KeyParseError>(())
947    /// ```
948    #[must_use]
949    pub fn split_str<'a>(&'a self, delimiter: &'a str) -> core::str::Split<'a, &'a str> {
950        self.inner.split(delimiter)
951    }
952
953    /// Returns the key with a prefix if it doesn't already have it
954    ///
955    /// This method efficiently adds a prefix to a key if it doesn't already
956    /// start with that prefix.
957    ///
958    /// # Arguments
959    ///
960    /// * `prefix` - The prefix to ensure is present
961    ///
962    /// # Examples
963    ///
964    /// ```rust
965    /// use domain_key::{Key, Domain, KeyDomain};
966    ///
967    /// #[derive(Debug)]
968    /// struct TestDomain;
969    /// impl Domain for TestDomain {
970    ///     const DOMAIN_NAME: &'static str = "test";
971    /// }
972    /// impl KeyDomain for TestDomain {}
973    /// type TestKey = Key<TestDomain>;
974    ///
975    /// let key = TestKey::new("profile")?;
976    /// let prefixed = key.ensure_prefix("user_")?;
977    /// assert_eq!(prefixed.as_str(), "user_profile");
978    ///
979    /// // If prefix already exists, returns the same key
980    /// let already_prefixed = prefixed.ensure_prefix("user_")?;
981    /// assert_eq!(already_prefixed.as_str(), "user_profile");
982    /// # Ok::<(), domain_key::KeyParseError>(())
983    /// ```
984    /// # Errors
985    ///
986    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
987    pub fn ensure_prefix(&self, prefix: &str) -> Result<Self, KeyParseError> {
988        if self.starts_with(prefix) {
989            return Ok(self.clone());
990        }
991
992        let new_len = prefix.len() + self.len();
993        if new_len > T::MAX_LENGTH {
994            return Err(KeyParseError::TooLong {
995                max_length: T::MAX_LENGTH,
996                actual_length: new_len,
997            });
998        }
999
1000        let result = utils::add_prefix_optimized(&self.inner, prefix);
1001
1002        // Quick validation of prefix only
1003        for (i, c) in prefix.chars().enumerate() {
1004            if !T::allowed_characters(c) {
1005                return Err(KeyParseError::InvalidCharacter {
1006                    character: c,
1007                    position: i,
1008                    expected: Some("allowed by domain"),
1009                });
1010            }
1011        }
1012
1013        T::validate_domain_rules(&result).map_err(Self::fix_domain_error)?;
1014
1015        let hash = Self::compute_hash(&result);
1016        let length = u32::try_from(new_len).map_err(|_| KeyParseError::TooLong {
1017            max_length: u32::MAX as usize,
1018            actual_length: new_len,
1019        })?;
1020
1021        Ok(Self {
1022            inner: result,
1023            hash,
1024            length,
1025            _marker: PhantomData,
1026        })
1027    }
1028
1029    /// Returns the key with a suffix if it doesn't already have it
1030    ///
1031    /// This method efficiently adds a suffix to a key if it doesn't already
1032    /// end with that suffix.
1033    ///
1034    /// # Arguments
1035    ///
1036    /// * `suffix` - The suffix to ensure is present
1037    ///
1038    /// # Examples
1039    ///
1040    /// ```rust
1041    /// use domain_key::{Key, Domain, KeyDomain};
1042    ///
1043    /// #[derive(Debug)]
1044    /// struct TestDomain;
1045    /// impl Domain for TestDomain {
1046    ///     const DOMAIN_NAME: &'static str = "test";
1047    /// }
1048    /// impl KeyDomain for TestDomain {}
1049    /// type TestKey = Key<TestDomain>;
1050    ///
1051    /// let key = TestKey::new("user")?;
1052    /// let suffixed = key.ensure_suffix("_profile")?;
1053    /// assert_eq!(suffixed.as_str(), "user_profile");
1054    ///
1055    /// // If suffix already exists, returns the same key
1056    /// let already_suffixed = suffixed.ensure_suffix("_profile")?;
1057    /// assert_eq!(already_suffixed.as_str(), "user_profile");
1058    /// # Ok::<(), domain_key::KeyParseError>(())
1059    /// ```
1060    /// # Errors
1061    ///
1062    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
1063    pub fn ensure_suffix(&self, suffix: &str) -> Result<Self, KeyParseError> {
1064        if self.ends_with(suffix) {
1065            return Ok(self.clone());
1066        }
1067
1068        let new_len = self.len() + suffix.len();
1069        if new_len > T::MAX_LENGTH {
1070            return Err(KeyParseError::TooLong {
1071                max_length: T::MAX_LENGTH,
1072                actual_length: new_len,
1073            });
1074        }
1075
1076        let result = utils::add_suffix_optimized(&self.inner, suffix);
1077
1078        // Quick validation of suffix only
1079        for (i, c) in suffix.chars().enumerate() {
1080            if !T::allowed_characters(c) {
1081                return Err(KeyParseError::InvalidCharacter {
1082                    character: c,
1083                    position: self.len() + i,
1084                    expected: Some("allowed by domain"),
1085                });
1086            }
1087        }
1088
1089        T::validate_domain_rules(&result).map_err(Self::fix_domain_error)?;
1090
1091        let hash = Self::compute_hash(&result);
1092        let length = new_len.try_into().map_err(|_| KeyParseError::TooLong {
1093            max_length: u32::MAX as usize,
1094            actual_length: new_len,
1095        })?;
1096
1097        Ok(Self {
1098            inner: result,
1099            hash,
1100            length,
1101            _marker: PhantomData,
1102        })
1103    }
1104
1105    /// Get validation rules that this key satisfies
1106    ///
1107    /// Returns detailed information about the validation characteristics
1108    /// of this key and its domain, useful for debugging and introspection.
1109    ///
1110    /// # Examples
1111    ///
1112    /// ```rust
1113    /// use domain_key::{Key, Domain, KeyDomain};
1114    ///
1115    /// #[derive(Debug)]
1116    /// struct TestDomain;
1117    /// impl Domain for TestDomain {
1118    ///     const DOMAIN_NAME: &'static str = "test";
1119    /// }
1120    /// impl KeyDomain for TestDomain {
1121    ///     const MAX_LENGTH: usize = 32;
1122    ///     const HAS_CUSTOM_VALIDATION: bool = true;
1123    /// }
1124    /// type TestKey = Key<TestDomain>;
1125    ///
1126    /// let key = TestKey::new("example")?;
1127    /// let info = key.validation_info();
1128    ///
1129    /// assert_eq!(info.domain_info.name, "test");
1130    /// assert_eq!(info.domain_info.max_length, 32);
1131    /// assert_eq!(info.length, 7);
1132    /// assert!(info.domain_info.has_custom_validation);
1133    /// # Ok::<(), domain_key::KeyParseError>(())
1134    /// ```
1135    #[must_use]
1136    pub fn validation_info(&self) -> KeyValidationInfo {
1137        KeyValidationInfo {
1138            domain_info: crate::domain::domain_info::<T>(),
1139            length: self.len(),
1140        }
1141    }
1142}
1143
1144// ============================================================================
1145// KEY IMPLEMENTATION - HELPER METHODS
1146// ============================================================================
1147
1148impl<T: KeyDomain> Key<T> {
1149    /// Fix domain name in domain validation errors
1150    ///
1151    /// This helper ensures that domain validation errors have the correct
1152    /// domain name, even when they're created generically.
1153    #[inline]
1154    fn fix_domain_error(e: KeyParseError) -> KeyParseError {
1155        match e {
1156            KeyParseError::DomainValidation { message, .. } => KeyParseError::DomainValidation {
1157                domain: T::DOMAIN_NAME,
1158                message,
1159            },
1160            other => other,
1161        }
1162    }
1163
1164    /// Common validation pipeline
1165    ///
1166    /// Performs validation that's common to all domains: length checking,
1167    /// character validation, and structural validation.
1168    ///
1169    /// # Errors
1170    ///
1171    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
1172    pub(crate) fn validate_common(key: &str) -> Result<(), KeyParseError> {
1173        let trimmed = key.trim();
1174
1175        if trimmed.is_empty() {
1176            return Err(KeyParseError::Empty);
1177        }
1178
1179        if trimmed.len() > T::MAX_LENGTH {
1180            return Err(KeyParseError::TooLong {
1181                max_length: T::MAX_LENGTH,
1182                actual_length: trimmed.len(),
1183            });
1184        }
1185
1186        if trimmed.len() < T::min_length() {
1187            return Err(KeyParseError::InvalidStructure {
1188                reason: "key is shorter than minimum required length",
1189            });
1190        }
1191
1192        // Use fast validation
1193        Self::validate_fast(trimmed)
1194    }
1195
1196    /// Fast validation path using optimized algorithms
1197    /// # Errors
1198    ///
1199    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
1200    fn validate_fast(key: &str) -> Result<(), KeyParseError> {
1201        let mut chars = key.char_indices();
1202        let mut prev_char = None;
1203
1204        // Validate first character
1205        if let Some((pos, first)) = chars.next() {
1206            let char_allowed = crate::utils::char_validation::is_key_char_fast(first)
1207                || T::allowed_start_character(first);
1208
1209            if !char_allowed {
1210                return Err(KeyParseError::InvalidCharacter {
1211                    character: first,
1212                    position: pos,
1213                    expected: Some("allowed by domain"),
1214                });
1215            }
1216
1217            prev_char = Some(first);
1218        }
1219
1220        // Validate remaining characters
1221        for (pos, c) in chars {
1222            let char_allowed =
1223                crate::utils::char_validation::is_key_char_fast(c) || T::allowed_characters(c);
1224
1225            if !char_allowed {
1226                return Err(KeyParseError::InvalidCharacter {
1227                    character: c,
1228                    position: pos,
1229                    expected: Some("allowed by domain"),
1230                });
1231            }
1232
1233            if let Some(prev) = prev_char {
1234                if !T::allowed_consecutive_characters(prev, c) {
1235                    return Err(KeyParseError::InvalidStructure {
1236                        reason: "consecutive characters not allowed",
1237                    });
1238                }
1239            }
1240            prev_char = Some(c);
1241        }
1242
1243        // Check last character
1244        if let Some(last) = prev_char {
1245            if !T::allowed_end_character(last) {
1246                return Err(KeyParseError::InvalidStructure {
1247                    reason: "invalid end character",
1248                });
1249            }
1250        }
1251
1252        Ok(())
1253    }
1254
1255    /// Normalize a borrowed string
1256    pub(crate) fn normalize(key: &str) -> Cow<'_, str> {
1257        let trimmed = key.trim();
1258
1259        let needs_lowercase =
1260            T::CASE_INSENSITIVE && trimmed.chars().any(|c| c.is_ascii_uppercase());
1261
1262        let base = if needs_lowercase {
1263            Cow::Owned(trimmed.to_ascii_lowercase())
1264        } else {
1265            // Borrow the trimmed slice — no allocation needed
1266            Cow::Borrowed(trimmed)
1267        };
1268
1269        // Apply domain-specific normalization
1270        T::normalize_domain(base)
1271    }
1272
1273    /// Normalize an owned string efficiently
1274    fn normalize_owned(mut key: String) -> String {
1275        // In-place trim: remove leading whitespace by draining, then truncate trailing
1276        let start = key.len() - key.trim_start().len();
1277        if start > 0 {
1278            key.drain(..start);
1279        }
1280        let trimmed_len = key.trim_end().len();
1281        key.truncate(trimmed_len);
1282
1283        if T::CASE_INSENSITIVE {
1284            key.make_ascii_lowercase();
1285        }
1286
1287        // Apply domain normalization
1288        match T::normalize_domain(Cow::Owned(key)) {
1289            Cow::Owned(s) => s,
1290            Cow::Borrowed(_) => unreachable!("We passed Cow::Owned"),
1291        }
1292    }
1293
1294    /// Compute hash using the configured algorithm
1295    ///
1296    /// The hash algorithm is selected at compile time based on feature flags,
1297    /// allowing for different performance/security trade-offs.
1298    pub(crate) fn compute_hash(key: &str) -> u64 {
1299        if key.is_empty() {
1300            return 0;
1301        }
1302
1303        Self::compute_hash_inner(key.as_bytes())
1304    }
1305
1306    /// Inner hash computation dispatched by feature flags
1307    ///
1308    /// Separated to keep each cfg branch as the sole return path,
1309    /// avoiding mixed `return` statements and dead-code warnings.
1310    fn compute_hash_inner(bytes: &[u8]) -> u64 {
1311        // Priority: fast > secure > crypto > default
1312
1313        #[cfg(feature = "fast")]
1314        {
1315            #[cfg(any(
1316                all(target_arch = "x86_64", target_feature = "aes"),
1317                all(
1318                    target_arch = "aarch64",
1319                    target_feature = "aes",
1320                    target_feature = "neon"
1321                )
1322            ))]
1323            {
1324                gxhash::gxhash64(bytes, 0)
1325            }
1326
1327            #[cfg(not(any(
1328                all(target_arch = "x86_64", target_feature = "aes"),
1329                all(
1330                    target_arch = "aarch64",
1331                    target_feature = "aes",
1332                    target_feature = "neon"
1333                )
1334            )))]
1335            {
1336                use core::hash::Hasher;
1337                let mut hasher = ahash::AHasher::default();
1338                hasher.write(bytes);
1339                hasher.finish()
1340            }
1341        }
1342
1343        #[cfg(all(feature = "secure", not(feature = "fast")))]
1344        {
1345            use core::hash::Hasher;
1346            let mut hasher = ahash::AHasher::default();
1347            hasher.write(bytes);
1348            hasher.finish()
1349        }
1350
1351        #[cfg(all(feature = "crypto", not(any(feature = "fast", feature = "secure"))))]
1352        {
1353            let hash = blake3::hash(bytes);
1354            let h = hash.as_bytes();
1355            u64::from_le_bytes([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]])
1356        }
1357
1358        #[cfg(not(any(feature = "fast", feature = "secure", feature = "crypto")))]
1359        {
1360            #[cfg(feature = "std")]
1361            {
1362                use core::hash::Hasher;
1363                use std::collections::hash_map::DefaultHasher;
1364                let mut hasher = DefaultHasher::new();
1365                hasher.write(bytes);
1366                hasher.finish()
1367            }
1368
1369            #[cfg(not(feature = "std"))]
1370            {
1371                Self::fnv1a_hash(bytes)
1372            }
1373        }
1374    }
1375
1376    /// FNV-1a hash implementation for `no_std` environments
1377    #[allow(dead_code)]
1378    fn fnv1a_hash(bytes: &[u8]) -> u64 {
1379        const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
1380        const FNV_PRIME: u64 = 0x0100_0000_01b3;
1381
1382        let mut hash = FNV_OFFSET_BASIS;
1383        for &byte in bytes {
1384            hash ^= u64::from(byte);
1385            hash = hash.wrapping_mul(FNV_PRIME);
1386        }
1387        hash
1388    }
1389}
1390
1391// ============================================================================
1392// SUPPORTING TYPES
1393// ============================================================================
1394
1395/// Information about a key's validation characteristics
1396///
1397/// This structure provides detailed information about how a key was validated
1398/// and what domain-specific features are enabled. Domain-level configuration
1399/// is available through the embedded [`DomainInfo`](crate::DomainInfo).
1400#[derive(Debug, Clone, PartialEq, Eq)]
1401pub struct KeyValidationInfo {
1402    /// Full domain configuration
1403    pub domain_info: crate::domain::DomainInfo,
1404    /// Actual length of the key
1405    pub length: usize,
1406}
1407
1408// ============================================================================
1409// STANDARD TRAIT IMPLEMENTATIONS
1410// ============================================================================
1411
1412/// Display implementation shows the key value
1413///
1414/// Outputs just the key string, consistent with `AsRef<str>`, `From<Key<T>> for String`,
1415/// and serde serialization. Use [`Key::domain`] separately when domain context is needed.
1416impl<T: KeyDomain> fmt::Display for Key<T> {
1417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1418        f.write_str(&self.inner)
1419    }
1420}
1421
1422/// `AsRef` implementation for string conversion
1423impl<T: KeyDomain> AsRef<str> for Key<T> {
1424    #[inline]
1425    fn as_ref(&self) -> &str {
1426        &self.inner
1427    }
1428}
1429
1430/// From implementation for converting to String
1431impl<T: KeyDomain> From<Key<T>> for String {
1432    fn from(key: Key<T>) -> Self {
1433        key.inner.into()
1434    }
1435}
1436
1437/// `TryFrom<String>` implementation for owned string conversion
1438///
1439/// This avoids re-borrowing through `&str` when you already have a `String`.
1440impl<T: KeyDomain> TryFrom<String> for Key<T> {
1441    type Error = KeyParseError;
1442
1443    fn try_from(s: String) -> Result<Self, Self::Error> {
1444        Key::from_string(s)
1445    }
1446}
1447
1448/// `TryFrom<&str>` implementation for borrowed string conversion
1449impl<T: KeyDomain> TryFrom<&str> for Key<T> {
1450    type Error = KeyParseError;
1451
1452    fn try_from(s: &str) -> Result<Self, Self::Error> {
1453        Key::new(s)
1454    }
1455}
1456
1457/// `FromStr` implementation for parsing from strings
1458impl<T: KeyDomain> FromStr for Key<T> {
1459    type Err = KeyParseError;
1460
1461    fn from_str(s: &str) -> Result<Self, Self::Err> {
1462        Key::new(s)
1463    }
1464}
1465
1466// ============================================================================
1467// TESTS
1468// ============================================================================
1469
1470#[cfg(test)]
1471mod tests {
1472    use super::*;
1473    use crate::domain::{DefaultDomain, Domain};
1474    #[cfg(not(feature = "std"))]
1475    use alloc::format;
1476    #[cfg(not(feature = "std"))]
1477    use alloc::string::ToString;
1478    #[cfg(not(feature = "std"))]
1479    use alloc::vec;
1480    #[cfg(not(feature = "std"))]
1481    use alloc::vec::Vec;
1482
1483    // Test domain
1484    #[derive(Debug)]
1485    struct TestDomain;
1486
1487    impl Domain for TestDomain {
1488        const DOMAIN_NAME: &'static str = "test";
1489    }
1490
1491    impl KeyDomain for TestDomain {
1492        const MAX_LENGTH: usize = 32;
1493        const HAS_CUSTOM_VALIDATION: bool = true;
1494        const HAS_CUSTOM_NORMALIZATION: bool = true;
1495        const CASE_INSENSITIVE: bool = true;
1496
1497        fn validate_domain_rules(key: &str) -> Result<(), KeyParseError> {
1498            if key.starts_with("invalid_") {
1499                return Err(KeyParseError::domain_error(
1500                    Self::DOMAIN_NAME,
1501                    "Keys cannot start with 'invalid_'",
1502                ));
1503            }
1504            Ok(())
1505        }
1506
1507        fn normalize_domain(key: Cow<'_, str>) -> Cow<'_, str> {
1508            if key.contains('-') {
1509                Cow::Owned(key.replace('-', "_"))
1510            } else {
1511                key
1512            }
1513        }
1514
1515        fn allowed_characters(c: char) -> bool {
1516            c.is_ascii_alphanumeric() || c == '_' || c == '-'
1517        }
1518
1519        fn validation_help() -> Option<&'static str> {
1520            Some("Use alphanumeric characters, underscores, and hyphens. Cannot start with 'invalid_'.")
1521        }
1522    }
1523
1524    type TestKey = Key<TestDomain>;
1525
1526    #[test]
1527    fn new_key_stores_value_and_domain() {
1528        let key = TestKey::new("valid_key").unwrap();
1529        assert_eq!(key.as_str(), "valid_key");
1530        assert_eq!(key.domain(), "test");
1531        assert_eq!(key.len(), 9);
1532    }
1533
1534    #[test]
1535    fn case_insensitive_domain_lowercases_and_normalizes() {
1536        let key = TestKey::new("Test-Key").unwrap();
1537        assert_eq!(key.as_str(), "test_key");
1538    }
1539
1540    #[test]
1541    fn domain_rules_reject_invalid_prefix() {
1542        let result = TestKey::new("invalid_key");
1543        assert!(result.is_err());
1544
1545        if let Err(KeyParseError::DomainValidation { domain, message }) = result {
1546            assert_eq!(domain, "test");
1547            assert!(message.contains("invalid_"));
1548        } else {
1549            panic!("Expected domain validation error");
1550        }
1551    }
1552
1553    #[test]
1554    fn rejects_empty_too_long_and_invalid_characters() {
1555        // Empty key
1556        assert!(matches!(TestKey::new(""), Err(KeyParseError::Empty)));
1557
1558        // Too long key
1559        let long_key = "a".repeat(50);
1560        assert!(matches!(
1561            TestKey::new(&long_key),
1562            Err(KeyParseError::TooLong {
1563                max_length: 32,
1564                actual_length: 50
1565            })
1566        ));
1567
1568        // Invalid character
1569        let result = TestKey::new("key with spaces");
1570        assert!(matches!(
1571            result,
1572            Err(KeyParseError::InvalidCharacter {
1573                character: ' ',
1574                position: 3,
1575                ..
1576            })
1577        ));
1578    }
1579
1580    #[test]
1581    fn equal_keys_produce_same_hash() {
1582        let key1 = TestKey::new("test_key").unwrap();
1583        let key2 = TestKey::new("test_key").unwrap();
1584
1585        // Same keys should have same hash
1586        assert_eq!(key1.hash(), key2.hash());
1587
1588        let key3 = TestKey::new("different_key").unwrap();
1589        // Different keys should have different hashes (with high probability)
1590        assert_ne!(key1.hash(), key3.hash());
1591    }
1592
1593    #[test]
1594    fn string_query_methods_work_correctly() {
1595        let key = TestKey::new("test_key_example").unwrap();
1596        assert!(key.starts_with("test_"));
1597        assert!(key.ends_with("_example"));
1598        assert!(key.contains("_key_"));
1599        assert_eq!(key.len(), 16);
1600        assert!(!key.is_empty());
1601    }
1602
1603    #[test]
1604    fn from_string_validates_owned_input() {
1605        let key = TestKey::from_string("test_key".to_string()).unwrap();
1606        assert_eq!(key.as_str(), "test_key");
1607    }
1608
1609    #[test]
1610    fn try_from_static_rejects_empty_string() {
1611        let key = TestKey::try_from_static("static_key").unwrap();
1612        assert_eq!(key.as_str(), "static_key");
1613
1614        let invalid = TestKey::try_from_static("");
1615        assert!(invalid.is_err());
1616    }
1617
1618    #[test]
1619    fn validation_info_reflects_domain_config() {
1620        let key = TestKey::new("test_key").unwrap();
1621        let info = key.validation_info();
1622
1623        assert_eq!(info.domain_info.name, "test");
1624        assert_eq!(info.domain_info.max_length, 32);
1625        assert_eq!(info.length, 8);
1626        assert!(info.domain_info.has_custom_validation);
1627        assert!(info.domain_info.has_custom_normalization);
1628    }
1629
1630    #[cfg(feature = "serde")]
1631    #[test]
1632    fn serde_roundtrip_preserves_key() {
1633        let key = TestKey::new("test_key").unwrap();
1634
1635        // Test JSON serialization
1636        let json = serde_json::to_string(&key).unwrap();
1637        assert_eq!(json, r#""test_key""#);
1638
1639        // Test JSON deserialization
1640        let deserialized: TestKey = serde_json::from_str(&json).unwrap();
1641        assert_eq!(deserialized, key);
1642    }
1643
1644    #[test]
1645    fn from_parts_joins_and_splits_roundtrip() {
1646        let key = TestKey::from_parts(&["user", "123", "profile"], "_").unwrap();
1647        assert_eq!(key.as_str(), "user_123_profile");
1648
1649        let parts: Vec<&str> = key.split('_').collect();
1650        assert_eq!(parts, vec!["user", "123", "profile"]);
1651    }
1652
1653    #[test]
1654    fn ensure_prefix_suffix_is_idempotent() {
1655        let key = TestKey::new("profile").unwrap();
1656
1657        let prefixed = key.ensure_prefix("user_").unwrap();
1658        assert_eq!(prefixed.as_str(), "user_profile");
1659
1660        // Already has prefix
1661        let same = prefixed.ensure_prefix("user_").unwrap();
1662        assert_eq!(same.as_str(), "user_profile");
1663
1664        let suffixed = key.ensure_suffix("_v1").unwrap();
1665        assert_eq!(suffixed.as_str(), "profile_v1");
1666
1667        // Already has suffix
1668        let same = suffixed.ensure_suffix("_v1").unwrap();
1669        assert_eq!(same.as_str(), "profile_v1");
1670    }
1671
1672    #[test]
1673    fn display_shows_raw_key_value() {
1674        let key = TestKey::new("example").unwrap();
1675        assert_eq!(format!("{key}"), "example");
1676    }
1677
1678    #[test]
1679    fn into_string_extracts_value() {
1680        let key = TestKey::new("example").unwrap();
1681        let string: String = key.into();
1682        assert_eq!(string, "example");
1683    }
1684
1685    #[test]
1686    fn parse_str_creates_validated_key() {
1687        let key: TestKey = "example".parse().unwrap();
1688        assert_eq!(key.as_str(), "example");
1689    }
1690
1691    #[test]
1692    fn default_domain_accepts_simple_keys() {
1693        type DefaultKey = Key<DefaultDomain>;
1694        let key = DefaultKey::new("test_key").unwrap();
1695        assert_eq!(key.domain(), "default");
1696        assert_eq!(key.as_str(), "test_key");
1697    }
1698
1699    #[test]
1700    fn len_returns_consistent_cached_value() {
1701        let key = TestKey::new("test_key").unwrap();
1702        // Length should be cached and O(1)
1703        assert_eq!(key.len(), 8);
1704        assert_eq!(key.len(), 8); // Second call should use cache
1705    }
1706
1707    #[test]
1708    fn split_methods_produce_same_parts() {
1709        let key = TestKey::new("user_profile_settings").unwrap();
1710
1711        let parts: Vec<&str> = key.split('_').collect();
1712        assert_eq!(parts, vec!["user", "profile", "settings"]);
1713
1714        let cached_parts: Vec<&str> = key.split_cached('_').collect();
1715        assert_eq!(cached_parts, vec!["user", "profile", "settings"]);
1716
1717        let str_parts: Vec<&str> = key.split_str("_").collect();
1718        assert_eq!(str_parts, vec!["user", "profile", "settings"]);
1719    }
1720}