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        #[expect(
523            clippy::cast_possible_truncation,
524            reason = "key length validated to fit in u32"
525        )]
526        let length = key.len() as u32;
527
528        Self {
529            inner: SmartString::from(key),
530            hash,
531            length,
532            _marker: PhantomData,
533        }
534    }
535
536    /// Creates a key from a static string with validation
537    ///
538    /// This is a safer alternative to `from_static_unchecked` that validates
539    /// the key at runtime. The validation cost is paid once, and subsequent
540    /// uses of the key are as fast as the unchecked version.
541    ///
542    /// # Arguments
543    ///
544    /// * `key` - A static string literal to validate and convert
545    ///
546    /// # Errors
547    ///
548    /// Returns `KeyParseError` if the static key fails validation
549    ///
550    /// # Examples
551    ///
552    /// ```rust
553    /// use domain_key::{Key, Domain, KeyDomain};
554    ///
555    /// #[derive(Debug)]
556    /// struct TestDomain;
557    /// impl Domain for TestDomain {
558    ///     const DOMAIN_NAME: &'static str = "test";
559    /// }
560    /// impl KeyDomain for TestDomain {}
561    /// type TestKey = Key<TestDomain>;
562    ///
563    /// let key = TestKey::try_from_static("static_key")?;
564    /// assert_eq!(key.as_str(), "static_key");
565    ///
566    /// let invalid = TestKey::try_from_static("");
567    /// assert!(invalid.is_err());
568    /// # Ok::<(), domain_key::KeyParseError>(())
569    /// ```
570    /// # Errors
571    ///
572    /// Returns `KeyParseError` if the constructed key fails validation
573    pub fn try_from_static(key: &'static str) -> Result<Self, KeyParseError> {
574        // Validate and create via the normal path
575        Self::new(key)
576    }
577
578    /// Try to create a key, returning None on validation failure
579    ///
580    /// This is a convenience method for when you want to handle validation
581    /// failures by ignoring invalid keys rather than handling errors.
582    ///
583    /// # Examples
584    ///
585    /// ```rust
586    /// use domain_key::{Key, Domain, KeyDomain};
587    ///
588    /// #[derive(Debug)]
589    /// struct TestDomain;
590    /// impl Domain for TestDomain {
591    ///     const DOMAIN_NAME: &'static str = "test";
592    /// }
593    /// impl KeyDomain for TestDomain {}
594    /// type TestKey = Key<TestDomain>;
595    ///
596    /// let valid = TestKey::try_new("valid_key").unwrap();
597    /// let invalid = TestKey::try_new(""); // Returns None
598    /// assert!(invalid.is_none());
599    /// ```
600    #[inline]
601    pub fn try_new(key: impl AsRef<str>) -> Option<Self> {
602        Self::new(key).ok()
603    }
604}
605
606// ============================================================================
607// KEY IMPLEMENTATION - ACCESSOR METHODS
608// ============================================================================
609
610impl<T: KeyDomain> Key<T> {
611    /// Returns the key as a string slice
612    ///
613    /// This is the primary way to access the string content of a key.
614    /// The returned reference is valid for the lifetime of the key.
615    ///
616    /// # Examples
617    ///
618    /// ```rust
619    /// use domain_key::{Key, Domain, KeyDomain};
620    ///
621    /// #[derive(Debug)]
622    /// struct TestDomain;
623    /// impl Domain for TestDomain {
624    ///     const DOMAIN_NAME: &'static str = "test";
625    /// }
626    /// impl KeyDomain for TestDomain {}
627    /// type TestKey = Key<TestDomain>;
628    ///
629    /// let key = TestKey::new("example")?;
630    /// assert_eq!(key.as_str(), "example");
631    /// # Ok::<(), domain_key::KeyParseError>(())
632    /// ```
633    #[inline]
634    #[must_use]
635    pub fn as_str(&self) -> &str {
636        &self.inner
637    }
638
639    /// Returns the domain name for this key type
640    ///
641    /// This is a compile-time constant that identifies which domain
642    /// this key belongs to.
643    ///
644    /// # Examples
645    ///
646    /// ```rust
647    /// use domain_key::{Key, Domain, KeyDomain};
648    ///
649    /// #[derive(Debug)]
650    /// struct UserDomain;
651    /// impl Domain for UserDomain {
652    ///     const DOMAIN_NAME: &'static str = "user";
653    /// }
654    /// impl KeyDomain for UserDomain {}
655    /// type UserKey = Key<UserDomain>;
656    ///
657    /// let key = UserKey::new("john")?;
658    /// assert_eq!(key.domain(), "user");
659    /// # Ok::<(), domain_key::KeyParseError>(())
660    /// ```
661    #[inline]
662    #[must_use]
663    pub const fn domain(&self) -> &'static str {
664        T::DOMAIN_NAME
665    }
666
667    /// Returns the length of the key string
668    ///
669    /// This is an O(1) operation using a cached length value.
670    ///
671    /// # Examples
672    ///
673    /// ```rust
674    /// use domain_key::{Key, Domain, KeyDomain};
675    ///
676    /// #[derive(Debug)]
677    /// struct TestDomain;
678    /// impl Domain for TestDomain {
679    ///     const DOMAIN_NAME: &'static str = "test";
680    /// }
681    /// impl KeyDomain for TestDomain {}
682    /// type TestKey = Key<TestDomain>;
683    ///
684    /// let key = TestKey::new("example")?;
685    /// assert_eq!(key.len(), 7);
686    /// # Ok::<(), domain_key::KeyParseError>(())
687    /// ```
688    #[inline]
689    #[must_use]
690    pub fn len(&self) -> usize {
691        self.length as usize
692    }
693
694    /// Returns true if the key is empty (this should never happen for valid keys)
695    ///
696    /// Since empty keys are rejected during validation, this method should
697    /// always return `false` for properly constructed keys. It's provided
698    /// for completeness and debugging purposes.
699    ///
700    /// # Examples
701    ///
702    /// ```rust
703    /// use domain_key::{Key, Domain, KeyDomain};
704    ///
705    /// #[derive(Debug)]
706    /// struct TestDomain;
707    /// impl Domain for TestDomain {
708    ///     const DOMAIN_NAME: &'static str = "test";
709    /// }
710    /// impl KeyDomain for TestDomain {}
711    /// type TestKey = Key<TestDomain>;
712    ///
713    /// let key = TestKey::new("example")?;
714    /// assert!(!key.is_empty());
715    /// # Ok::<(), domain_key::KeyParseError>(())
716    /// ```
717    #[inline]
718    #[must_use]
719    pub fn is_empty(&self) -> bool {
720        self.length == 0
721    }
722
723    /// Returns the cached hash value
724    ///
725    /// This hash is computed once during key creation and cached for the
726    /// lifetime of the key. It's used internally for hash-based collections
727    /// and can be useful for custom hash-based data structures.
728    ///
729    /// **Note:** The hash algorithm depends on the active feature flags
730    /// (`fast`, `secure`, `crypto`, or the default hasher). Keys created
731    /// with different feature configurations will produce different hash
732    /// values. Do not persist or compare hash values across builds with
733    /// different features.
734    ///
735    /// # Examples
736    ///
737    /// ```rust
738    /// use domain_key::{Key, Domain, KeyDomain};
739    ///
740    /// #[derive(Debug)]
741    /// struct TestDomain;
742    /// impl Domain for TestDomain {
743    ///     const DOMAIN_NAME: &'static str = "test";
744    /// }
745    /// impl KeyDomain for TestDomain {}
746    /// type TestKey = Key<TestDomain>;
747    ///
748    /// let key1 = TestKey::new("example")?;
749    /// let key2 = TestKey::new("example")?;
750    /// let key3 = TestKey::new("different")?;
751    ///
752    /// // Same keys have same hash
753    /// assert_eq!(key1.hash(), key2.hash());
754    /// // Different keys have different hashes (with high probability)
755    /// assert_ne!(key1.hash(), key3.hash());
756    /// # Ok::<(), domain_key::KeyParseError>(())
757    /// ```
758    #[inline]
759    #[must_use]
760    pub const fn hash(&self) -> u64 {
761        self.hash
762    }
763
764    /// Checks if this key starts with the given prefix
765    ///
766    /// This is a simple string prefix check that can be useful for
767    /// categorizing or filtering keys.
768    ///
769    /// # Arguments
770    ///
771    /// * `prefix` - The prefix string to check for
772    ///
773    /// # Examples
774    ///
775    /// ```rust
776    /// use domain_key::{Key, Domain, KeyDomain};
777    ///
778    /// #[derive(Debug)]
779    /// struct TestDomain;
780    /// impl Domain for TestDomain {
781    ///     const DOMAIN_NAME: &'static str = "test";
782    /// }
783    /// impl KeyDomain for TestDomain {}
784    /// type TestKey = Key<TestDomain>;
785    ///
786    /// let key = TestKey::new("user_profile")?;
787    /// assert!(key.starts_with("user_"));
788    /// assert!(!key.starts_with("admin_"));
789    /// # Ok::<(), domain_key::KeyParseError>(())
790    /// ```
791    #[inline]
792    #[must_use]
793    pub fn starts_with(&self, prefix: &str) -> bool {
794        self.inner.starts_with(prefix)
795    }
796
797    /// Checks if this key ends with the given suffix
798    ///
799    /// This is a simple string suffix check that can be useful for
800    /// categorizing or filtering keys.
801    ///
802    /// # Arguments
803    ///
804    /// * `suffix` - The suffix string to check for
805    ///
806    /// # Examples
807    ///
808    /// ```rust
809    /// use domain_key::{Key, Domain, KeyDomain};
810    ///
811    /// #[derive(Debug)]
812    /// struct TestDomain;
813    /// impl Domain for TestDomain {
814    ///     const DOMAIN_NAME: &'static str = "test";
815    /// }
816    /// impl KeyDomain for TestDomain {}
817    /// type TestKey = Key<TestDomain>;
818    ///
819    /// let key = TestKey::new("user_profile")?;
820    /// assert!(key.ends_with("_profile"));
821    /// assert!(!key.ends_with("_settings"));
822    /// # Ok::<(), domain_key::KeyParseError>(())
823    /// ```
824    #[inline]
825    #[must_use]
826    pub fn ends_with(&self, suffix: &str) -> bool {
827        self.inner.ends_with(suffix)
828    }
829
830    /// Checks if this key contains the given substring
831    ///
832    /// This performs a substring search within the key.
833    ///
834    /// # Arguments
835    ///
836    /// * `pattern` - The substring to search for
837    ///
838    /// # Examples
839    ///
840    /// ```rust
841    /// use domain_key::{Key, Domain, KeyDomain};
842    ///
843    /// #[derive(Debug)]
844    /// struct TestDomain;
845    /// impl Domain for TestDomain {
846    ///     const DOMAIN_NAME: &'static str = "test";
847    /// }
848    /// impl KeyDomain for TestDomain {}
849    /// type TestKey = Key<TestDomain>;
850    ///
851    /// let key = TestKey::new("user_profile_settings")?;
852    /// assert!(key.contains("profile"));
853    /// assert!(!key.contains("admin"));
854    /// # Ok::<(), domain_key::KeyParseError>(())
855    /// ```
856    #[inline]
857    #[must_use]
858    pub fn contains(&self, pattern: &str) -> bool {
859        self.inner.contains(pattern)
860    }
861
862    /// Returns an iterator over the characters of the key
863    ///
864    /// This provides access to individual characters in the key string.
865    ///
866    /// # Examples
867    ///
868    /// ```rust
869    /// use domain_key::{Key, Domain, KeyDomain};
870    ///
871    /// #[derive(Debug)]
872    /// struct TestDomain;
873    /// impl Domain for TestDomain {
874    ///     const DOMAIN_NAME: &'static str = "test";
875    /// }
876    /// impl KeyDomain for TestDomain {}
877    /// type TestKey = Key<TestDomain>;
878    ///
879    /// let key = TestKey::new("abc")?;
880    /// let chars: Vec<char> = key.chars().collect();
881    /// assert_eq!(chars, vec!['a', 'b', 'c']);
882    /// # Ok::<(), domain_key::KeyParseError>(())
883    /// ```
884    #[inline]
885    pub fn chars(&self) -> core::str::Chars<'_> {
886        self.inner.chars()
887    }
888
889    /// Splits the key by a delimiter and returns an iterator
890    ///
891    /// This method provides consistent split functionality.
892    ///
893    /// # Arguments
894    ///
895    /// * `delimiter` - Character to split on
896    ///
897    /// # Examples
898    ///
899    /// ```rust
900    /// use domain_key::{Key, Domain, KeyDomain};
901    ///
902    /// #[derive(Debug)]
903    /// struct TestDomain;
904    /// impl Domain for TestDomain {
905    ///     const DOMAIN_NAME: &'static str = "test";
906    /// }
907    /// impl KeyDomain for TestDomain {}
908    /// type TestKey = Key<TestDomain>;
909    ///
910    /// let key = TestKey::new("user_profile_settings")?;
911    /// let parts: Vec<&str> = key.split('_').collect();
912    /// assert_eq!(parts, vec!["user", "profile", "settings"]);
913    /// # Ok::<(), domain_key::KeyParseError>(())
914    /// ```
915    #[must_use]
916    pub fn split(&self, delimiter: char) -> SplitIterator<'_> {
917        SplitIterator(utils::new_split_cache(&self.inner, delimiter))
918    }
919
920    /// Split operation for consistent API
921    ///
922    /// This method provides the same functionality as `split()` but with explicit naming
923    /// for cases where caching behavior needs to be clear.
924    #[must_use]
925    pub fn split_cached(&self, delimiter: char) -> SplitCache<'_> {
926        utils::new_split_cache(&self.inner, delimiter)
927    }
928
929    /// Splits the key by a string delimiter and returns an iterator
930    ///
931    /// This method splits the key using a string pattern rather than a single character.
932    ///
933    /// # Examples
934    ///
935    /// ```rust
936    /// use domain_key::{Key, Domain, KeyDomain};
937    ///
938    /// #[derive(Debug)]
939    /// struct TestDomain;
940    /// impl Domain for TestDomain {
941    ///     const DOMAIN_NAME: &'static str = "test";
942    /// }
943    /// impl KeyDomain for TestDomain {}
944    /// type TestKey = Key<TestDomain>;
945    ///
946    /// let key = TestKey::new("user-and-profile-and-settings")?;
947    /// let parts: Vec<&str> = key.split_str("-and-").collect();
948    /// assert_eq!(parts, vec!["user", "profile", "settings"]);
949    /// # Ok::<(), domain_key::KeyParseError>(())
950    /// ```
951    #[must_use]
952    pub fn split_str<'a>(&'a self, delimiter: &'a str) -> core::str::Split<'a, &'a str> {
953        self.inner.split(delimiter)
954    }
955
956    /// Returns the key with a prefix if it doesn't already have it
957    ///
958    /// This method efficiently adds a prefix to a key if it doesn't already
959    /// start with that prefix.
960    ///
961    /// # Arguments
962    ///
963    /// * `prefix` - The prefix to ensure is present
964    ///
965    /// # Examples
966    ///
967    /// ```rust
968    /// use domain_key::{Key, Domain, KeyDomain};
969    ///
970    /// #[derive(Debug)]
971    /// struct TestDomain;
972    /// impl Domain for TestDomain {
973    ///     const DOMAIN_NAME: &'static str = "test";
974    /// }
975    /// impl KeyDomain for TestDomain {}
976    /// type TestKey = Key<TestDomain>;
977    ///
978    /// let key = TestKey::new("profile")?;
979    /// let prefixed = key.ensure_prefix("user_")?;
980    /// assert_eq!(prefixed.as_str(), "user_profile");
981    ///
982    /// // If prefix already exists, returns the same key
983    /// let already_prefixed = prefixed.ensure_prefix("user_")?;
984    /// assert_eq!(already_prefixed.as_str(), "user_profile");
985    /// # Ok::<(), domain_key::KeyParseError>(())
986    /// ```
987    /// # Errors
988    ///
989    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
990    pub fn ensure_prefix(&self, prefix: &str) -> Result<Self, KeyParseError> {
991        if self.starts_with(prefix) {
992            return Ok(self.clone());
993        }
994
995        let new_len = prefix.len() + self.len();
996        if new_len > T::MAX_LENGTH {
997            return Err(KeyParseError::TooLong {
998                max_length: T::MAX_LENGTH,
999                actual_length: new_len,
1000            });
1001        }
1002
1003        let result = utils::add_prefix_optimized(&self.inner, prefix);
1004
1005        // Quick validation of prefix only
1006        for (i, c) in prefix.chars().enumerate() {
1007            if !T::allowed_characters(c) {
1008                return Err(KeyParseError::InvalidCharacter {
1009                    character: c,
1010                    position: i,
1011                    expected: Some("allowed by domain"),
1012                });
1013            }
1014        }
1015
1016        T::validate_domain_rules(&result).map_err(Self::fix_domain_error)?;
1017
1018        let hash = Self::compute_hash(&result);
1019        let length = u32::try_from(new_len).map_err(|_| KeyParseError::TooLong {
1020            max_length: u32::MAX as usize,
1021            actual_length: new_len,
1022        })?;
1023
1024        Ok(Self {
1025            inner: result,
1026            hash,
1027            length,
1028            _marker: PhantomData,
1029        })
1030    }
1031
1032    /// Returns the key with a suffix if it doesn't already have it
1033    ///
1034    /// This method efficiently adds a suffix to a key if it doesn't already
1035    /// end with that suffix.
1036    ///
1037    /// # Arguments
1038    ///
1039    /// * `suffix` - The suffix to ensure is present
1040    ///
1041    /// # Examples
1042    ///
1043    /// ```rust
1044    /// use domain_key::{Key, Domain, KeyDomain};
1045    ///
1046    /// #[derive(Debug)]
1047    /// struct TestDomain;
1048    /// impl Domain for TestDomain {
1049    ///     const DOMAIN_NAME: &'static str = "test";
1050    /// }
1051    /// impl KeyDomain for TestDomain {}
1052    /// type TestKey = Key<TestDomain>;
1053    ///
1054    /// let key = TestKey::new("user")?;
1055    /// let suffixed = key.ensure_suffix("_profile")?;
1056    /// assert_eq!(suffixed.as_str(), "user_profile");
1057    ///
1058    /// // If suffix already exists, returns the same key
1059    /// let already_suffixed = suffixed.ensure_suffix("_profile")?;
1060    /// assert_eq!(already_suffixed.as_str(), "user_profile");
1061    /// # Ok::<(), domain_key::KeyParseError>(())
1062    /// ```
1063    /// # Errors
1064    ///
1065    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
1066    pub fn ensure_suffix(&self, suffix: &str) -> Result<Self, KeyParseError> {
1067        if self.ends_with(suffix) {
1068            return Ok(self.clone());
1069        }
1070
1071        let new_len = self.len() + suffix.len();
1072        if new_len > T::MAX_LENGTH {
1073            return Err(KeyParseError::TooLong {
1074                max_length: T::MAX_LENGTH,
1075                actual_length: new_len,
1076            });
1077        }
1078
1079        let result = utils::add_suffix_optimized(&self.inner, suffix);
1080
1081        // Quick validation of suffix only
1082        for (i, c) in suffix.chars().enumerate() {
1083            if !T::allowed_characters(c) {
1084                return Err(KeyParseError::InvalidCharacter {
1085                    character: c,
1086                    position: self.len() + i,
1087                    expected: Some("allowed by domain"),
1088                });
1089            }
1090        }
1091
1092        T::validate_domain_rules(&result).map_err(Self::fix_domain_error)?;
1093
1094        let hash = Self::compute_hash(&result);
1095        let length = new_len.try_into().map_err(|_| KeyParseError::TooLong {
1096            max_length: u32::MAX as usize,
1097            actual_length: new_len,
1098        })?;
1099
1100        Ok(Self {
1101            inner: result,
1102            hash,
1103            length,
1104            _marker: PhantomData,
1105        })
1106    }
1107
1108    /// Get validation rules that this key satisfies
1109    ///
1110    /// Returns detailed information about the validation characteristics
1111    /// of this key and its domain, useful for debugging and introspection.
1112    ///
1113    /// # Examples
1114    ///
1115    /// ```rust
1116    /// use domain_key::{Key, Domain, KeyDomain};
1117    ///
1118    /// #[derive(Debug)]
1119    /// struct TestDomain;
1120    /// impl Domain for TestDomain {
1121    ///     const DOMAIN_NAME: &'static str = "test";
1122    /// }
1123    /// impl KeyDomain for TestDomain {
1124    ///     const MAX_LENGTH: usize = 32;
1125    ///     const HAS_CUSTOM_VALIDATION: bool = true;
1126    /// }
1127    /// type TestKey = Key<TestDomain>;
1128    ///
1129    /// let key = TestKey::new("example")?;
1130    /// let info = key.validation_info();
1131    ///
1132    /// assert_eq!(info.domain_info.name, "test");
1133    /// assert_eq!(info.domain_info.max_length, 32);
1134    /// assert_eq!(info.length, 7);
1135    /// assert!(info.domain_info.has_custom_validation);
1136    /// # Ok::<(), domain_key::KeyParseError>(())
1137    /// ```
1138    #[must_use]
1139    pub fn validation_info(&self) -> KeyValidationInfo {
1140        KeyValidationInfo {
1141            domain_info: crate::domain::domain_info::<T>(),
1142            length: self.len(),
1143        }
1144    }
1145}
1146
1147// ============================================================================
1148// KEY IMPLEMENTATION - HELPER METHODS
1149// ============================================================================
1150
1151impl<T: KeyDomain> Key<T> {
1152    /// Fix domain name in domain validation errors
1153    ///
1154    /// This helper ensures that domain validation errors have the correct
1155    /// domain name, even when they're created generically.
1156    #[inline]
1157    fn fix_domain_error(e: KeyParseError) -> KeyParseError {
1158        match e {
1159            KeyParseError::DomainValidation { message, .. } => KeyParseError::DomainValidation {
1160                domain: T::DOMAIN_NAME,
1161                message,
1162            },
1163            other => other,
1164        }
1165    }
1166
1167    /// Common validation pipeline
1168    ///
1169    /// Performs validation that's common to all domains: length checking,
1170    /// character validation, and structural validation.
1171    ///
1172    /// # Errors
1173    ///
1174    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
1175    pub(crate) fn validate_common(key: &str) -> Result<(), KeyParseError> {
1176        let trimmed = key.trim();
1177
1178        if trimmed.is_empty() {
1179            return Err(KeyParseError::Empty);
1180        }
1181
1182        if trimmed.len() > T::MAX_LENGTH {
1183            return Err(KeyParseError::TooLong {
1184                max_length: T::MAX_LENGTH,
1185                actual_length: trimmed.len(),
1186            });
1187        }
1188
1189        if trimmed.len() < T::min_length() {
1190            return Err(KeyParseError::InvalidStructure {
1191                reason: "key is shorter than minimum required length",
1192            });
1193        }
1194
1195        // Use fast validation
1196        Self::validate_fast(trimmed)
1197    }
1198
1199    /// Fast validation path using optimized algorithms
1200    /// # Errors
1201    ///
1202    /// Returns `KeyParseError` if the prefixed key would be invalid or too long
1203    fn validate_fast(key: &str) -> Result<(), KeyParseError> {
1204        let mut chars = key.char_indices();
1205        let mut prev_char = None;
1206
1207        // Validate first character
1208        if let Some((pos, first)) = chars.next() {
1209            let char_allowed = crate::utils::char_validation::is_key_char_fast(first)
1210                || T::allowed_start_character(first);
1211
1212            if !char_allowed {
1213                return Err(KeyParseError::InvalidCharacter {
1214                    character: first,
1215                    position: pos,
1216                    expected: Some("allowed by domain"),
1217                });
1218            }
1219
1220            prev_char = Some(first);
1221        }
1222
1223        // Validate remaining characters
1224        for (pos, c) in chars {
1225            let char_allowed =
1226                crate::utils::char_validation::is_key_char_fast(c) || T::allowed_characters(c);
1227
1228            if !char_allowed {
1229                return Err(KeyParseError::InvalidCharacter {
1230                    character: c,
1231                    position: pos,
1232                    expected: Some("allowed by domain"),
1233                });
1234            }
1235
1236            if let Some(prev) = prev_char {
1237                if !T::allowed_consecutive_characters(prev, c) {
1238                    return Err(KeyParseError::InvalidStructure {
1239                        reason: "consecutive characters not allowed",
1240                    });
1241                }
1242            }
1243            prev_char = Some(c);
1244        }
1245
1246        // Check last character
1247        if let Some(last) = prev_char {
1248            if !T::allowed_end_character(last) {
1249                return Err(KeyParseError::InvalidStructure {
1250                    reason: "invalid end character",
1251                });
1252            }
1253        }
1254
1255        Ok(())
1256    }
1257
1258    /// Normalize a borrowed string
1259    pub(crate) fn normalize(key: &str) -> Cow<'_, str> {
1260        let trimmed = key.trim();
1261
1262        let needs_lowercase =
1263            T::CASE_INSENSITIVE && trimmed.chars().any(|c| c.is_ascii_uppercase());
1264
1265        let base = if needs_lowercase {
1266            Cow::Owned(trimmed.to_ascii_lowercase())
1267        } else {
1268            // Borrow the trimmed slice — no allocation needed
1269            Cow::Borrowed(trimmed)
1270        };
1271
1272        // Apply domain-specific normalization
1273        T::normalize_domain(base)
1274    }
1275
1276    /// Normalize an owned string efficiently
1277    fn normalize_owned(mut key: String) -> String {
1278        // In-place trim: remove leading whitespace by draining, then truncate trailing
1279        let start = key.len() - key.trim_start().len();
1280        if start > 0 {
1281            key.drain(..start);
1282        }
1283        let trimmed_len = key.trim_end().len();
1284        key.truncate(trimmed_len);
1285
1286        if T::CASE_INSENSITIVE {
1287            key.make_ascii_lowercase();
1288        }
1289
1290        // Apply domain normalization
1291        match T::normalize_domain(Cow::Owned(key)) {
1292            Cow::Owned(s) => s,
1293            Cow::Borrowed(_) => unreachable!("We passed Cow::Owned"),
1294        }
1295    }
1296
1297    /// Compute hash using the configured algorithm
1298    ///
1299    /// The hash algorithm is selected at compile time based on feature flags,
1300    /// allowing for different performance/security trade-offs.
1301    pub(crate) fn compute_hash(key: &str) -> u64 {
1302        if key.is_empty() {
1303            return 0;
1304        }
1305
1306        Self::compute_hash_inner(key.as_bytes())
1307    }
1308
1309    /// Inner hash computation dispatched by feature flags
1310    ///
1311    /// Separated to keep each cfg branch as the sole return path,
1312    /// avoiding mixed `return` statements and dead-code warnings.
1313    fn compute_hash_inner(bytes: &[u8]) -> u64 {
1314        // Priority: fast > secure > crypto > default
1315
1316        #[cfg(feature = "fast")]
1317        {
1318            #[cfg(any(
1319                all(target_arch = "x86_64", target_feature = "aes"),
1320                all(
1321                    target_arch = "aarch64",
1322                    target_feature = "aes",
1323                    target_feature = "neon"
1324                )
1325            ))]
1326            {
1327                gxhash::gxhash64(bytes, 0)
1328            }
1329
1330            #[cfg(not(any(
1331                all(target_arch = "x86_64", target_feature = "aes"),
1332                all(
1333                    target_arch = "aarch64",
1334                    target_feature = "aes",
1335                    target_feature = "neon"
1336                )
1337            )))]
1338            {
1339                use core::hash::Hasher;
1340                let mut hasher = ahash::AHasher::default();
1341                hasher.write(bytes);
1342                hasher.finish()
1343            }
1344        }
1345
1346        #[cfg(all(feature = "secure", not(feature = "fast")))]
1347        {
1348            use core::hash::Hasher;
1349            let mut hasher = ahash::AHasher::default();
1350            hasher.write(bytes);
1351            hasher.finish()
1352        }
1353
1354        #[cfg(all(feature = "crypto", not(any(feature = "fast", feature = "secure"))))]
1355        {
1356            let hash = blake3::hash(bytes);
1357            let h = hash.as_bytes();
1358            u64::from_le_bytes([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]])
1359        }
1360
1361        #[cfg(not(any(feature = "fast", feature = "secure", feature = "crypto")))]
1362        {
1363            #[cfg(feature = "std")]
1364            {
1365                use core::hash::Hasher;
1366                use std::collections::hash_map::DefaultHasher;
1367                let mut hasher = DefaultHasher::new();
1368                hasher.write(bytes);
1369                hasher.finish()
1370            }
1371
1372            #[cfg(not(feature = "std"))]
1373            {
1374                Self::fnv1a_hash(bytes)
1375            }
1376        }
1377    }
1378
1379    /// FNV-1a hash implementation for `no_std` environments
1380    #[expect(
1381        dead_code,
1382        reason = "fallback hash used only when no hash feature is enabled"
1383    )]
1384    fn fnv1a_hash(bytes: &[u8]) -> u64 {
1385        const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
1386        const FNV_PRIME: u64 = 0x0100_0000_01b3;
1387
1388        let mut hash = FNV_OFFSET_BASIS;
1389        for &byte in bytes {
1390            hash ^= u64::from(byte);
1391            hash = hash.wrapping_mul(FNV_PRIME);
1392        }
1393        hash
1394    }
1395}
1396
1397// ============================================================================
1398// SUPPORTING TYPES
1399// ============================================================================
1400
1401/// Information about a key's validation characteristics
1402///
1403/// This structure provides detailed information about how a key was validated
1404/// and what domain-specific features are enabled. Domain-level configuration
1405/// is available through the embedded [`DomainInfo`](crate::DomainInfo).
1406#[derive(Debug, Clone, PartialEq, Eq)]
1407pub struct KeyValidationInfo {
1408    /// Full domain configuration
1409    pub domain_info: crate::domain::DomainInfo,
1410    /// Actual length of the key
1411    pub length: usize,
1412}
1413
1414// ============================================================================
1415// STANDARD TRAIT IMPLEMENTATIONS
1416// ============================================================================
1417
1418/// Display implementation shows the key value
1419///
1420/// Outputs just the key string, consistent with `AsRef<str>`, `From<Key<T>> for String`,
1421/// and serde serialization. Use [`Key::domain`] separately when domain context is needed.
1422impl<T: KeyDomain> fmt::Display for Key<T> {
1423    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1424        f.write_str(&self.inner)
1425    }
1426}
1427
1428/// `AsRef` implementation for string conversion
1429impl<T: KeyDomain> AsRef<str> for Key<T> {
1430    #[inline]
1431    fn as_ref(&self) -> &str {
1432        &self.inner
1433    }
1434}
1435
1436/// From implementation for converting to String
1437impl<T: KeyDomain> From<Key<T>> for String {
1438    fn from(key: Key<T>) -> Self {
1439        key.inner.into()
1440    }
1441}
1442
1443/// `TryFrom<String>` implementation for owned string conversion
1444///
1445/// This avoids re-borrowing through `&str` when you already have a `String`.
1446impl<T: KeyDomain> TryFrom<String> for Key<T> {
1447    type Error = KeyParseError;
1448
1449    fn try_from(s: String) -> Result<Self, Self::Error> {
1450        Key::from_string(s)
1451    }
1452}
1453
1454/// `TryFrom<&str>` implementation for borrowed string conversion
1455impl<T: KeyDomain> TryFrom<&str> for Key<T> {
1456    type Error = KeyParseError;
1457
1458    fn try_from(s: &str) -> Result<Self, Self::Error> {
1459        Key::new(s)
1460    }
1461}
1462
1463/// `FromStr` implementation for parsing from strings
1464impl<T: KeyDomain> FromStr for Key<T> {
1465    type Err = KeyParseError;
1466
1467    fn from_str(s: &str) -> Result<Self, Self::Err> {
1468        Key::new(s)
1469    }
1470}
1471
1472// ============================================================================
1473// TESTS
1474// ============================================================================
1475
1476#[cfg(test)]
1477mod tests {
1478    use super::*;
1479    use crate::domain::{DefaultDomain, Domain};
1480    #[cfg(not(feature = "std"))]
1481    use alloc::format;
1482    #[cfg(not(feature = "std"))]
1483    use alloc::string::ToString;
1484    #[cfg(not(feature = "std"))]
1485    use alloc::vec;
1486    #[cfg(not(feature = "std"))]
1487    use alloc::vec::Vec;
1488
1489    // Test domain
1490    #[derive(Debug)]
1491    struct TestDomain;
1492
1493    impl Domain for TestDomain {
1494        const DOMAIN_NAME: &'static str = "test";
1495    }
1496
1497    impl KeyDomain for TestDomain {
1498        const MAX_LENGTH: usize = 32;
1499        const HAS_CUSTOM_VALIDATION: bool = true;
1500        const HAS_CUSTOM_NORMALIZATION: bool = true;
1501        const CASE_INSENSITIVE: bool = true;
1502
1503        fn validate_domain_rules(key: &str) -> Result<(), KeyParseError> {
1504            if key.starts_with("invalid_") {
1505                return Err(KeyParseError::domain_error(
1506                    Self::DOMAIN_NAME,
1507                    "Keys cannot start with 'invalid_'",
1508                ));
1509            }
1510            Ok(())
1511        }
1512
1513        fn normalize_domain(key: Cow<'_, str>) -> Cow<'_, str> {
1514            if key.contains('-') {
1515                Cow::Owned(key.replace('-', "_"))
1516            } else {
1517                key
1518            }
1519        }
1520
1521        fn allowed_characters(c: char) -> bool {
1522            c.is_ascii_alphanumeric() || c == '_' || c == '-'
1523        }
1524
1525        fn validation_help() -> Option<&'static str> {
1526            Some("Use alphanumeric characters, underscores, and hyphens. Cannot start with 'invalid_'.")
1527        }
1528    }
1529
1530    type TestKey = Key<TestDomain>;
1531
1532    #[test]
1533    fn new_key_stores_value_and_domain() {
1534        let key = TestKey::new("valid_key").unwrap();
1535        assert_eq!(key.as_str(), "valid_key");
1536        assert_eq!(key.domain(), "test");
1537        assert_eq!(key.len(), 9);
1538    }
1539
1540    #[test]
1541    fn case_insensitive_domain_lowercases_and_normalizes() {
1542        let key = TestKey::new("Test-Key").unwrap();
1543        assert_eq!(key.as_str(), "test_key");
1544    }
1545
1546    #[test]
1547    fn domain_rules_reject_invalid_prefix() {
1548        let result = TestKey::new("invalid_key");
1549        assert!(result.is_err());
1550
1551        if let Err(KeyParseError::DomainValidation { domain, message }) = result {
1552            assert_eq!(domain, "test");
1553            assert!(message.contains("invalid_"));
1554        } else {
1555            panic!("Expected domain validation error");
1556        }
1557    }
1558
1559    #[test]
1560    fn rejects_empty_too_long_and_invalid_characters() {
1561        // Empty key
1562        assert!(matches!(TestKey::new(""), Err(KeyParseError::Empty)));
1563
1564        // Too long key
1565        let long_key = "a".repeat(50);
1566        assert!(matches!(
1567            TestKey::new(&long_key),
1568            Err(KeyParseError::TooLong {
1569                max_length: 32,
1570                actual_length: 50
1571            })
1572        ));
1573
1574        // Invalid character
1575        let result = TestKey::new("key with spaces");
1576        assert!(matches!(
1577            result,
1578            Err(KeyParseError::InvalidCharacter {
1579                character: ' ',
1580                position: 3,
1581                ..
1582            })
1583        ));
1584    }
1585
1586    #[test]
1587    fn equal_keys_produce_same_hash() {
1588        let key1 = TestKey::new("test_key").unwrap();
1589        let key2 = TestKey::new("test_key").unwrap();
1590
1591        // Same keys should have same hash
1592        assert_eq!(key1.hash(), key2.hash());
1593
1594        let key3 = TestKey::new("different_key").unwrap();
1595        // Different keys should have different hashes (with high probability)
1596        assert_ne!(key1.hash(), key3.hash());
1597    }
1598
1599    #[test]
1600    fn string_query_methods_work_correctly() {
1601        let key = TestKey::new("test_key_example").unwrap();
1602        assert!(key.starts_with("test_"));
1603        assert!(key.ends_with("_example"));
1604        assert!(key.contains("_key_"));
1605        assert_eq!(key.len(), 16);
1606        assert!(!key.is_empty());
1607    }
1608
1609    #[test]
1610    fn from_string_validates_owned_input() {
1611        let key = TestKey::from_string("test_key".to_string()).unwrap();
1612        assert_eq!(key.as_str(), "test_key");
1613    }
1614
1615    #[test]
1616    fn try_from_static_rejects_empty_string() {
1617        let key = TestKey::try_from_static("static_key").unwrap();
1618        assert_eq!(key.as_str(), "static_key");
1619
1620        let invalid = TestKey::try_from_static("");
1621        assert!(invalid.is_err());
1622    }
1623
1624    #[test]
1625    fn validation_info_reflects_domain_config() {
1626        let key = TestKey::new("test_key").unwrap();
1627        let info = key.validation_info();
1628
1629        assert_eq!(info.domain_info.name, "test");
1630        assert_eq!(info.domain_info.max_length, 32);
1631        assert_eq!(info.length, 8);
1632        assert!(info.domain_info.has_custom_validation);
1633        assert!(info.domain_info.has_custom_normalization);
1634    }
1635
1636    #[cfg(feature = "serde")]
1637    #[test]
1638    fn serde_roundtrip_preserves_key() {
1639        let key = TestKey::new("test_key").unwrap();
1640
1641        // Test JSON serialization
1642        let json = serde_json::to_string(&key).unwrap();
1643        assert_eq!(json, r#""test_key""#);
1644
1645        // Test JSON deserialization
1646        let deserialized: TestKey = serde_json::from_str(&json).unwrap();
1647        assert_eq!(deserialized, key);
1648    }
1649
1650    #[test]
1651    fn from_parts_joins_and_splits_roundtrip() {
1652        let key = TestKey::from_parts(&["user", "123", "profile"], "_").unwrap();
1653        assert_eq!(key.as_str(), "user_123_profile");
1654
1655        let parts: Vec<&str> = key.split('_').collect();
1656        assert_eq!(parts, vec!["user", "123", "profile"]);
1657    }
1658
1659    #[test]
1660    fn ensure_prefix_suffix_is_idempotent() {
1661        let key = TestKey::new("profile").unwrap();
1662
1663        let prefixed = key.ensure_prefix("user_").unwrap();
1664        assert_eq!(prefixed.as_str(), "user_profile");
1665
1666        // Already has prefix
1667        let same = prefixed.ensure_prefix("user_").unwrap();
1668        assert_eq!(same.as_str(), "user_profile");
1669
1670        let suffixed = key.ensure_suffix("_v1").unwrap();
1671        assert_eq!(suffixed.as_str(), "profile_v1");
1672
1673        // Already has suffix
1674        let same = suffixed.ensure_suffix("_v1").unwrap();
1675        assert_eq!(same.as_str(), "profile_v1");
1676    }
1677
1678    #[test]
1679    fn display_shows_raw_key_value() {
1680        let key = TestKey::new("example").unwrap();
1681        assert_eq!(format!("{key}"), "example");
1682    }
1683
1684    #[test]
1685    fn into_string_extracts_value() {
1686        let key = TestKey::new("example").unwrap();
1687        let string: String = key.into();
1688        assert_eq!(string, "example");
1689    }
1690
1691    #[test]
1692    fn parse_str_creates_validated_key() {
1693        let key: TestKey = "example".parse().unwrap();
1694        assert_eq!(key.as_str(), "example");
1695    }
1696
1697    #[test]
1698    fn default_domain_accepts_simple_keys() {
1699        type DefaultKey = Key<DefaultDomain>;
1700        let key = DefaultKey::new("test_key").unwrap();
1701        assert_eq!(key.domain(), "default");
1702        assert_eq!(key.as_str(), "test_key");
1703    }
1704
1705    #[test]
1706    fn len_returns_consistent_cached_value() {
1707        let key = TestKey::new("test_key").unwrap();
1708        // Length should be cached and O(1)
1709        assert_eq!(key.len(), 8);
1710        assert_eq!(key.len(), 8); // Second call should use cache
1711    }
1712
1713    #[test]
1714    fn split_methods_produce_same_parts() {
1715        let key = TestKey::new("user_profile_settings").unwrap();
1716
1717        let parts: Vec<&str> = key.split('_').collect();
1718        assert_eq!(parts, vec!["user", "profile", "settings"]);
1719
1720        let cached_parts: Vec<&str> = key.split_cached('_').collect();
1721        assert_eq!(cached_parts, vec!["user", "profile", "settings"]);
1722
1723        let str_parts: Vec<&str> = key.split_str("_").collect();
1724        assert_eq!(str_parts, vec!["user", "profile", "settings"]);
1725    }
1726}