short_id/
lib.rs

1//! A tiny crate for generating short, URL-safe, unique identifiers.
2//!
3//! Unlike full UUIDs (which are 36 characters and include hyphens), `short-id` gives you
4//! compact 14-character strings that are easy to copy, paste, and use in URLs.
5//!
6//! # Goals
7//!
8//! 1. **Make it very easy to generate short random IDs** for things like request IDs,
9//!    user-facing tokens, test data, and log correlation.
10//!
11//! 2. **Provide an optional "ordered" variant** where IDs include a timestamp prefix,
12//!    so when you sort them as strings they roughly follow creation time.
13//!
14//! This crate is intentionally minimal - no configuration, no custom alphabets, no complex API.
15//!
16//! # Quick Start
17//!
18//! ```
19//! use short_id::short_id;
20//!
21//! // Generate a random ID
22//! let id = short_id();
23//! println!("Request ID: {}", id);
24//! // Example output: "X7K9mP2nQwE-Tg"
25//! ```
26//!
27//! For time-ordered IDs:
28//!
29//! ```
30//! use short_id::short_id_ordered;
31//!
32//! let id1 = short_id_ordered();
33//! std::thread::sleep(std::time::Duration::from_millis(100));
34//! let id2 = short_id_ordered();
35//!
36//! // IDs from different times are different
37//! assert_ne!(id1, id2);
38//! ```
39//!
40//! # Use Cases
41//!
42//! - Request IDs for logging and tracing
43//! - User-facing tokens and session IDs
44//! - Test data generation
45//! - Short URLs and resource identifiers
46//! - Any place you want something shorter and simpler than UUIDs
47//!
48//! # Characteristics
49//!
50//! - **Length**: Always exactly 14 characters (default)
51//! - **URL-safe**: Only `A-Z`, `a-z`, `0-9`, `-`, `_` (no special characters)
52//! - **Cryptographically secure**: Uses `OsRng` for random bytes
53//! - **No configuration needed**: Just call the function
54//!
55//! # Advanced: Custom Length IDs
56//!
57//! For advanced use cases, you can control the ID length by specifying the number of random bytes:
58//!
59//! ```
60//! use short_id::{short_id_with_bytes, short_id_ordered_with_bytes};
61//!
62//! // Generate a shorter 8-character ID (6 bytes)
63//! let short = short_id_with_bytes(6);
64//! assert_eq!(short.len(), 8);
65//!
66//! // Generate a longer 22-character ID (16 bytes)
67//! let long = short_id_with_bytes(16);
68//! assert_eq!(long.len(), 22);
69//!
70//! // Time-ordered IDs also support custom lengths
71//! let ordered = short_id_ordered_with_bytes(12);
72//! ```
73//!
74//! **When to use custom lengths:**
75//!
76//! - **Fewer bytes (e.g., 4-6)**: Use for low-volume applications where you need very short IDs
77//!   and collision risk is acceptable. Keep in mind that 6 bytes provides only ~48 bits of entropy.
78//!
79//! - **Default (10 bytes)**: Recommended for most applications. Provides ~80 bits of entropy
80//!   with 14-character IDs. The [`short_id()`] and [`short_id_ordered()`] functions use this.
81//!
82//! - **More bytes (e.g., 16-32)**: Use for high-volume applications or when you need extra
83//!   safety margin. 16 bytes provides ~128 bits of entropy.
84//!
85//! **Important:** Using fewer bytes significantly increases collision probability. For most users,
86//! the default [`short_id()`] and [`short_id_ordered()`] functions are recommended.
87//!
88//! # Features
89//!
90//! - **`std`** (enabled by default): Enables [`short_id_ordered()`] and [`short_id_ordered_with_bytes()`]
91//!   which need `std::time::SystemTime`
92//!
93//! For `no_std` environments with `alloc`:
94//!
95//! ```toml
96//! [dependencies]
97//! short-id = { version = "0.4", default-features = false }
98//! ```
99//!
100//! In `no_std` mode, only [`short_id()`] and [`short_id_with_bytes()`] are available.
101
102#![cfg_attr(not(feature = "std"), no_std)]
103
104#[cfg(not(feature = "std"))]
105extern crate alloc;
106
107#[cfg(not(feature = "std"))]
108use alloc::string::String;
109
110#[cfg(not(feature = "std"))]
111use alloc::vec;
112
113#[cfg(feature = "std")]
114use std::vec;
115
116use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
117use rand::{rngs::OsRng, RngCore};
118
119/// Maximum number of random bytes allowed for custom-length ID generation.
120///
121/// This limit prevents excessive memory allocation and ensures reasonable ID sizes.
122const MAX_BYTES: usize = 32;
123
124/// Convenience macro for generating a random short ID.
125///
126/// This macro simply calls [`short_id()`] and is provided for ergonomics.
127///
128/// # Examples
129///
130/// ```
131/// use short_id::id;
132///
133/// let request_id = id!();
134/// assert_eq!(request_id.len(), 14);
135/// ```
136#[macro_export]
137macro_rules! id {
138    () => {
139        $crate::short_id()
140    };
141}
142
143/// Convenience macro for generating a time-ordered short ID.
144///
145/// This macro simply calls [`short_id_ordered()`] and is provided for ergonomics.
146/// Requires the `std` feature (enabled by default).
147///
148/// # Examples
149///
150/// ```
151/// use short_id::ordered_id;
152///
153/// let log_id = ordered_id!();
154/// assert_eq!(log_id.len(), 14);
155/// ```
156#[cfg(feature = "std")]
157#[macro_export]
158macro_rules! ordered_id {
159    () => {
160        $crate::short_id_ordered()
161    };
162}
163
164/// Internal helper: generates a random ID with the specified number of bytes.
165///
166/// # Panics
167///
168/// Panics if `num_bytes` is 0 or exceeds `MAX_BYTES`.
169fn generate_random_id(num_bytes: usize) -> String {
170    assert!(num_bytes > 0, "num_bytes must be greater than 0");
171    assert!(
172        num_bytes <= MAX_BYTES,
173        "num_bytes must not exceed {} (got {})",
174        MAX_BYTES,
175        num_bytes
176    );
177
178    let mut bytes = vec![0u8; num_bytes];
179    OsRng.fill_bytes(&mut bytes);
180    URL_SAFE_NO_PAD.encode(&bytes)
181}
182
183/// Generates a random, URL-safe short ID.
184///
185/// Creates a 14-character ID from 10 cryptographically secure random bytes,
186/// encoded with base64url (no padding).
187///
188/// # Examples
189///
190/// Basic usage:
191///
192/// ```
193/// use short_id::short_id;
194///
195/// let id = short_id();
196/// assert_eq!(id.len(), 14);
197/// ```
198///
199/// Use for request IDs:
200///
201/// ```
202/// use short_id::short_id;
203///
204/// fn handle_request() -> String {
205///     let request_id = short_id();
206///     println!("Processing request {}", request_id);
207///     request_id
208/// }
209///
210/// let id = handle_request();
211/// assert_eq!(id.len(), 14);
212/// ```
213///
214/// Generate multiple unique IDs:
215///
216/// ```
217/// use short_id::short_id;
218///
219/// let ids: Vec<String> = (0..10).map(|_| short_id()).collect();
220///
221/// // All IDs are unique
222/// for i in 0..ids.len() {
223///     for j in i+1..ids.len() {
224///         assert_ne!(ids[i], ids[j]);
225///     }
226/// }
227/// ```
228///
229/// IDs are URL-safe:
230///
231/// ```
232/// use short_id::short_id;
233///
234/// let id = short_id();
235/// let url = format!("https://example.com/resource/{}", id);
236/// // No encoding needed - safe to use directly
237/// ```
238pub fn short_id() -> String {
239    generate_random_id(10)
240}
241
242/// Internal helper: generates a time-ordered ID with the specified number of bytes.
243///
244/// Uses 8 bytes for timestamp and fills the remaining bytes with random data.
245///
246/// # Panics
247///
248/// Panics if `num_bytes` is less than 8, is 0, or exceeds `MAX_BYTES`.
249#[cfg(feature = "std")]
250fn generate_ordered_id(num_bytes: usize) -> String {
251    assert!(
252        num_bytes >= 8,
253        "num_bytes must be at least 8 for ordered IDs (got {})",
254        num_bytes
255    );
256    assert!(
257        num_bytes <= MAX_BYTES,
258        "num_bytes must not exceed {} (got {})",
259        MAX_BYTES,
260        num_bytes
261    );
262
263    let timestamp_us = std::time::SystemTime::now()
264        .duration_since(std::time::UNIX_EPOCH)
265        .expect("system time before Unix epoch")
266        .as_micros() as u64;
267
268    let mut bytes = vec![0u8; num_bytes];
269    bytes[0..8].copy_from_slice(&timestamp_us.to_be_bytes());
270    OsRng.fill_bytes(&mut bytes[8..]);
271
272    URL_SAFE_NO_PAD.encode(&bytes)
273}
274
275/// Generates a time-ordered, URL-safe short ID.
276///
277/// Creates a 14-character ID with microsecond-precision timestamp for excellent time
278/// resolution when generating IDs in rapid succession. The ID consists of:
279/// - First 8 bytes: Unix timestamp (microseconds since epoch) as big-endian u64
280/// - Next 2 bytes: Cryptographically secure random bytes
281///
282/// With microsecond precision, IDs created within the same microsecond will differ
283/// by their random component (65,536 possible values per microsecond).
284///
285/// **This function requires the `std` feature** (enabled by default).
286///
287/// # Examples
288///
289/// Basic usage:
290///
291/// ```
292/// use short_id::short_id_ordered;
293///
294/// let id = short_id_ordered();
295/// assert_eq!(id.len(), 14);
296/// ```
297///
298/// IDs from different times differ:
299///
300/// ```
301/// use short_id::short_id_ordered;
302///
303/// let id1 = short_id_ordered();
304/// std::thread::sleep(std::time::Duration::from_millis(100));
305/// let id2 = short_id_ordered();
306///
307/// // IDs generated at different times are different
308/// assert_ne!(id1, id2);
309/// ```
310///
311/// Even within the same second, IDs are unique:
312///
313/// ```
314/// use short_id::short_id_ordered;
315///
316/// let ids: Vec<String> = (0..10).map(|_| short_id_ordered()).collect();
317///
318/// // All unique due to random component
319/// for i in 0..ids.len() {
320///     for j in i+1..ids.len() {
321///         assert_ne!(ids[i], ids[j]);
322///     }
323/// }
324/// ```
325///
326/// Use for log entries:
327///
328/// ```
329/// use short_id::short_id_ordered;
330///
331/// struct LogEntry {
332///     id: String,
333///     message: String,
334/// }
335///
336/// impl LogEntry {
337///     fn new(message: String) -> Self {
338///         LogEntry {
339///             id: short_id_ordered(),
340///             message,
341///         }
342///     }
343/// }
344///
345/// let log = LogEntry::new("Started processing".to_string());
346/// assert_eq!(log.id.len(), 14);
347/// ```
348#[cfg(feature = "std")]
349pub fn short_id_ordered() -> String {
350    generate_ordered_id(10)
351}
352
353/// **Advanced:** Generates a random, URL-safe short ID with a custom number of bytes.
354///
355/// This is an advanced API that allows you to control the ID length by specifying
356/// the number of random bytes to use. The ID is encoded using URL-safe base64 without
357/// padding, so the resulting string length will be approximately `(num_bytes * 4) / 3`.
358///
359/// **For most users, [`short_id()`] is the recommended API.**
360///
361/// # Parameters
362///
363/// - `num_bytes`: Number of random bytes to generate (1 to 32 inclusive)
364///
365/// # Panics
366///
367/// Panics if `num_bytes` is 0 or exceeds 32.
368///
369/// # Security Note
370///
371/// **Using fewer bytes reduces entropy and increases collision probability.**
372/// - 10 bytes (default): ~80 bits of entropy, collision probability ~1 in 10^24
373/// - 6 bytes: ~48 bits of entropy, collision probability ~1 in 10^14
374/// - 4 bytes: ~32 bits of entropy, collision probability ~1 in 4 billion
375///
376/// Choose an appropriate size based on your uniqueness requirements and expected scale.
377///
378/// # Examples
379///
380/// Generate a standard 14-character ID (equivalent to `short_id()`):
381///
382/// ```
383/// use short_id::short_id_with_bytes;
384///
385/// let id = short_id_with_bytes(10);
386/// assert_eq!(id.len(), 14);
387/// ```
388///
389/// Generate a shorter 8-character ID with less entropy:
390///
391/// ```
392/// use short_id::short_id_with_bytes;
393///
394/// let short_id = short_id_with_bytes(6);
395/// assert_eq!(short_id.len(), 8);
396/// // Suitable for small-scale applications with fewer expected IDs
397/// ```
398///
399/// Generate a longer ID with more entropy:
400///
401/// ```
402/// use short_id::short_id_with_bytes;
403///
404/// let long_id = short_id_with_bytes(16);
405/// assert_eq!(long_id.len(), 22);
406/// // Extra safety margin for high-volume applications
407/// ```
408///
409/// All IDs are URL-safe regardless of size:
410///
411/// ```
412/// use short_id::short_id_with_bytes;
413///
414/// let id = short_id_with_bytes(6);
415/// let url = format!("https://example.com/resource/{}", id);
416/// // No encoding needed - safe to use directly
417/// ```
418pub fn short_id_with_bytes(num_bytes: usize) -> String {
419    generate_random_id(num_bytes)
420}
421
422/// **Advanced:** Generates a time-ordered, URL-safe short ID with a custom number of bytes.
423///
424/// This is an advanced API that allows you to control the ID length by specifying
425/// the number of bytes to use. The first 8 bytes always contain a microsecond-precision
426/// timestamp, and the remaining bytes are filled with cryptographically secure random data.
427///
428/// **For most users, [`short_id_ordered()`] is the recommended API.**
429///
430/// **This function requires the `std` feature** (enabled by default).
431///
432/// # Parameters
433///
434/// - `num_bytes`: Total number of bytes for the ID (8 to 32 inclusive, must be at least 8 for the timestamp)
435///
436/// # Panics
437///
438/// Panics if `num_bytes` is less than 8 or exceeds 32.
439///
440/// # Security Note
441///
442/// **Using fewer random bytes (beyond the 8-byte timestamp) reduces uniqueness within the same microsecond.**
443/// - 10 bytes (default): 8 bytes timestamp + 2 bytes random (~16 bits randomness per microsecond)
444/// - 8 bytes: timestamp only, no randomness (IDs in the same microsecond will collide!)
445/// - 16 bytes: 8 bytes timestamp + 8 bytes random (~64 bits randomness per microsecond)
446///
447/// # Examples
448///
449/// Generate a standard time-ordered ID (equivalent to `short_id_ordered()`):
450///
451/// ```
452/// use short_id::short_id_ordered_with_bytes;
453///
454/// let id = short_id_ordered_with_bytes(10);
455/// assert_eq!(id.len(), 14);
456/// ```
457///
458/// IDs from different times contain different timestamps:
459///
460/// ```
461/// use short_id::short_id_ordered_with_bytes;
462///
463/// let id1 = short_id_ordered_with_bytes(10);
464/// std::thread::sleep(std::time::Duration::from_millis(10));
465/// let id2 = short_id_ordered_with_bytes(10);
466///
467/// // IDs from different times are different
468/// assert_ne!(id1, id2);
469/// ```
470///
471/// Shorter time-ordered IDs with minimal randomness:
472///
473/// ```
474/// use short_id::short_id_ordered_with_bytes;
475///
476/// let id = short_id_ordered_with_bytes(8);
477/// assert_eq!(id.len(), 11);
478/// // Warning: No random component! Only suitable if you never generate
479/// // multiple IDs within the same microsecond.
480/// ```
481///
482/// Longer time-ordered IDs with extra randomness:
483///
484/// ```
485/// use short_id::short_id_ordered_with_bytes;
486///
487/// let id = short_id_ordered_with_bytes(16);
488/// assert_eq!(id.len(), 22);
489/// // 8 bytes random component provides excellent uniqueness
490/// // even when generating millions of IDs per second
491/// ```
492#[cfg(feature = "std")]
493pub fn short_id_ordered_with_bytes(num_bytes: usize) -> String {
494    generate_ordered_id(num_bytes)
495}
496
497#[cfg(test)]
498mod tests {
499    use super::*;
500
501    #[test]
502    fn test_short_id_length() {
503        let id = short_id();
504        assert_eq!(id.len(), 14);
505    }
506
507    #[test]
508    fn test_short_id_unique() {
509        let id1 = short_id();
510        let id2 = short_id();
511        assert_ne!(id1, id2);
512    }
513
514    #[test]
515    fn test_short_id_url_safe() {
516        for _ in 0..100 {
517            let id = short_id();
518            assert!(!id.contains('+'));
519            assert!(!id.contains('/'));
520            assert!(!id.contains('='));
521        }
522    }
523
524    #[test]
525    fn test_many_unique_ids() {
526        // Generate many IDs and ensure all are unique
527        #[cfg(feature = "std")]
528        {
529            let ids: Vec<String> = (0..1000).map(|_| short_id()).collect();
530            let unique_count = ids.iter().collect::<std::collections::HashSet<_>>().len();
531            assert_eq!(unique_count, 1000);
532        }
533
534        #[cfg(not(feature = "std"))]
535        {
536            // In no_std, just verify a few IDs are unique
537            let id1 = short_id();
538            let id2 = short_id();
539            let id3 = short_id();
540            assert_ne!(id1, id2);
541            assert_ne!(id2, id3);
542            assert_ne!(id1, id3);
543        }
544    }
545
546    #[cfg(feature = "std")]
547    #[test]
548    fn test_short_id_ordered_length() {
549        let id = short_id_ordered();
550        assert_eq!(id.len(), 14);
551    }
552
553    #[cfg(feature = "std")]
554    #[test]
555    fn test_short_id_ordered_unique() {
556        let id1 = short_id_ordered();
557        let id2 = short_id_ordered();
558        assert_ne!(id1, id2);
559    }
560
561    #[cfg(feature = "std")]
562    #[test]
563    fn test_short_id_ordered_includes_timestamp() {
564        // Generate IDs and verify they contain timestamp information
565        // by checking they change over time
566        let id1 = short_id_ordered();
567        std::thread::sleep(std::time::Duration::from_secs(1));
568        let id2 = short_id_ordered();
569
570        // IDs from different times should differ
571        assert_ne!(id1, id2);
572    }
573
574    #[cfg(feature = "std")]
575    #[test]
576    fn test_short_id_ordered_url_safe() {
577        for _ in 0..100 {
578            let id = short_id_ordered();
579            assert!(!id.contains('+'));
580            assert!(!id.contains('/'));
581            assert!(!id.contains('='));
582        }
583    }
584
585    // Tests for short_id_with_bytes
586
587    #[test]
588    fn test_short_id_with_bytes_standard() {
589        let id = short_id_with_bytes(10);
590        assert_eq!(id.len(), 14);
591    }
592
593    #[test]
594    fn test_short_id_with_bytes_shorter() {
595        let id = short_id_with_bytes(6);
596        assert_eq!(id.len(), 8);
597    }
598
599    #[test]
600    fn test_short_id_with_bytes_longer() {
601        let id = short_id_with_bytes(16);
602        assert_eq!(id.len(), 22);
603    }
604
605    #[test]
606    fn test_short_id_with_bytes_url_safe() {
607        for num_bytes in [6, 10, 16, 32] {
608            let id = short_id_with_bytes(num_bytes);
609            assert!(!id.contains('+'));
610            assert!(!id.contains('/'));
611            assert!(!id.contains('='));
612        }
613    }
614
615    #[test]
616    fn test_short_id_with_bytes_unique() {
617        // Generate many IDs with different byte counts
618        for num_bytes in [6, 10, 16] {
619            let id1 = short_id_with_bytes(num_bytes);
620            let id2 = short_id_with_bytes(num_bytes);
621            assert_ne!(id1, id2);
622        }
623    }
624
625    #[test]
626    #[should_panic(expected = "num_bytes must be greater than 0")]
627    fn test_short_id_with_bytes_zero_panics() {
628        short_id_with_bytes(0);
629    }
630
631    #[test]
632    #[should_panic(expected = "num_bytes must not exceed 32")]
633    fn test_short_id_with_bytes_too_large_panics() {
634        short_id_with_bytes(33);
635    }
636
637    // Tests for short_id_ordered_with_bytes
638
639    #[cfg(feature = "std")]
640    #[test]
641    fn test_short_id_ordered_with_bytes_standard() {
642        let id = short_id_ordered_with_bytes(10);
643        assert_eq!(id.len(), 14);
644    }
645
646    #[cfg(feature = "std")]
647    #[test]
648    fn test_short_id_ordered_with_bytes_minimal() {
649        let id = short_id_ordered_with_bytes(8);
650        assert_eq!(id.len(), 11);
651    }
652
653    #[cfg(feature = "std")]
654    #[test]
655    fn test_short_id_ordered_with_bytes_longer() {
656        let id = short_id_ordered_with_bytes(16);
657        assert_eq!(id.len(), 22);
658    }
659
660    #[cfg(feature = "std")]
661    #[test]
662    fn test_short_id_ordered_with_bytes_url_safe() {
663        for num_bytes in [8, 10, 16, 32] {
664            let id = short_id_ordered_with_bytes(num_bytes);
665            assert!(!id.contains('+'));
666            assert!(!id.contains('/'));
667            assert!(!id.contains('='));
668        }
669    }
670
671    #[cfg(feature = "std")]
672    #[test]
673    fn test_short_id_ordered_with_bytes_includes_timestamp() {
674        // Generate IDs with different byte sizes and verify they contain timestamp information
675        // by checking that IDs generated at different times are different
676        for num_bytes in [8, 10, 16] {
677            let id1 = short_id_ordered_with_bytes(num_bytes);
678            std::thread::sleep(std::time::Duration::from_secs(1));
679            let id2 = short_id_ordered_with_bytes(num_bytes);
680
681            // IDs from different times should differ
682            assert_ne!(id1, id2, "IDs from different times should be different");
683        }
684    }
685
686    #[cfg(feature = "std")]
687    #[test]
688    fn test_short_id_ordered_with_bytes_unique() {
689        // Even with same timestamp, random component makes them unique
690        for num_bytes in [10, 16] {
691            let id1 = short_id_ordered_with_bytes(num_bytes);
692            let id2 = short_id_ordered_with_bytes(num_bytes);
693            assert_ne!(id1, id2);
694        }
695    }
696
697    #[cfg(feature = "std")]
698    #[test]
699    #[should_panic(expected = "num_bytes must be at least 8 for ordered IDs")]
700    fn test_short_id_ordered_with_bytes_too_small_panics() {
701        short_id_ordered_with_bytes(7);
702    }
703
704    #[cfg(feature = "std")]
705    #[test]
706    #[should_panic(expected = "num_bytes must not exceed 32")]
707    fn test_short_id_ordered_with_bytes_too_large_panics() {
708        short_id_ordered_with_bytes(33);
709    }
710}
711
712/// A newtype wrapper around a short ID string.
713///
714/// Provides a typed interface for working with short IDs, with methods for
715/// generation and conversion. The inner string is always a valid 14-character
716/// URL-safe identifier.
717///
718/// # Examples
719///
720/// ```
721/// use short_id::ShortId;
722///
723/// // Generate a random ID
724/// let id = ShortId::random();
725/// assert_eq!(id.as_str().len(), 14);
726///
727/// // Convert to string
728/// let s: String = id.into_string();
729/// ```
730#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
731pub struct ShortId(String);
732
733impl ShortId {
734    /// Creates a new random short ID.
735    ///
736    /// This is equivalent to calling [`short_id()`] but returns a typed [`ShortId`].
737    ///
738    /// # Examples
739    ///
740    /// ```
741    /// use short_id::ShortId;
742    ///
743    /// let id = ShortId::random();
744    /// assert_eq!(id.as_str().len(), 14);
745    /// ```
746    pub fn random() -> Self {
747        ShortId(short_id())
748    }
749
750    /// Creates a new time-ordered short ID.
751    ///
752    /// This is equivalent to calling [`short_id_ordered()`] but returns a typed [`ShortId`].
753    /// Requires the `std` feature (enabled by default).
754    ///
755    /// # Examples
756    ///
757    /// ```
758    /// use short_id::ShortId;
759    ///
760    /// let id = ShortId::ordered();
761    /// assert_eq!(id.as_str().len(), 14);
762    /// ```
763    #[cfg(feature = "std")]
764    pub fn ordered() -> Self {
765        ShortId(short_id_ordered())
766    }
767
768    /// Returns the ID as a string slice.
769    ///
770    /// # Examples
771    ///
772    /// ```
773    /// use short_id::ShortId;
774    ///
775    /// let id = ShortId::random();
776    /// let s: &str = id.as_str();
777    /// assert_eq!(s.len(), 14);
778    /// ```
779    pub fn as_str(&self) -> &str {
780        &self.0
781    }
782
783    /// Consumes the `ShortId` and returns the inner `String`.
784    ///
785    /// # Examples
786    ///
787    /// ```
788    /// use short_id::ShortId;
789    ///
790    /// let id = ShortId::random();
791    /// let s: String = id.into_string();
792    /// assert_eq!(s.len(), 14);
793    /// ```
794    pub fn into_string(self) -> String {
795        self.0
796    }
797}
798
799impl core::fmt::Display for ShortId {
800    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
801        write!(f, "{}", self.0)
802    }
803}
804
805impl AsRef<str> for ShortId {
806    fn as_ref(&self) -> &str {
807        &self.0
808    }
809}
810
811impl From<String> for ShortId {
812    fn from(s: String) -> Self {
813        ShortId(s)
814    }
815}
816
817impl From<ShortId> for String {
818    fn from(id: ShortId) -> Self {
819        id.0
820    }
821}