Skip to main content

nsi_ffi_wrap/
token.rs

1//! Token type for attribute and type name strings.
2//!
3//! The `Token` type wraps attribute/type name strings with different backing
4//! implementations based on feature flags:
5//!
6//! - Default (`Ustr`): Interned strings with zero-cost C pointer conversion
7//! - `cstring_tokens`: Uses `CString` for non-interned C strings
8//!
9//! Note: Unlike handles which change frequently, tokens (attribute names,
10//! type names) are typically repeated many times, making interning beneficial
11//! by default.
12
13use std::ffi::c_char;
14
15// ─── Ustr backing (interned strings, default) ───────────────────────────────
16
17#[cfg(not(feature = "cstring_tokens"))]
18mod inner {
19    use super::*;
20    use ustr::{Ustr, ustr};
21
22    /// A token string backed by an interned `Ustr`.
23    ///
24    /// This provides O(1) equality checks and zero-cost conversion to C strings.
25    /// This is the default because tokens (attribute names, type names) are
26    /// typically repeated many times in a scene, making interning beneficial.
27    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28    pub struct Token(Ustr);
29
30    impl Token {
31        /// Create a new token from a string slice.
32        #[inline(always)]
33        pub fn new(s: &str) -> Self {
34            Self(ustr(s))
35        }
36
37        /// Get the token as a string slice.
38        #[inline(always)]
39        pub fn as_str(&self) -> &str {
40            self.0.as_str()
41        }
42
43        /// Get the token as a C string pointer.
44        ///
45        /// The returned pointer is valid for the lifetime of the `Ustr` cache
46        /// (effectively 'static for interned strings).
47        #[inline(always)]
48        pub fn as_char_ptr(&self) -> *const c_char {
49            self.0.as_char_ptr()
50        }
51    }
52
53    impl From<&str> for Token {
54        #[inline(always)]
55        fn from(s: &str) -> Self {
56            Self::new(s)
57        }
58    }
59
60    impl PartialEq<Ustr> for Token {
61        fn eq(&self, other: &Ustr) -> bool {
62            self.0 == *other
63        }
64    }
65}
66
67// ─── CString backing (owned C strings) ──────────────────────────────────────
68
69#[cfg(feature = "cstring_tokens")]
70mod inner {
71    use super::*;
72    use std::ffi::CString;
73
74    /// A token string backed by an owned `CString`.
75    ///
76    /// This provides conversion to C strings without interning.
77    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
78    pub struct Token(CString);
79
80    impl Token {
81        /// Create a new token from a string slice.
82        ///
83        /// # Panics
84        /// Panics if the string contains interior NUL bytes.
85        #[inline(always)]
86        pub fn new(s: &str) -> Self {
87            Self(CString::new(s).expect("Token string contains NUL byte"))
88        }
89
90        /// Get the token as a string slice.
91        #[inline(always)]
92        pub fn as_str(&self) -> &str {
93            self.0.to_str().expect("Token contains invalid UTF-8")
94        }
95
96        /// Get the token as a C string pointer.
97        ///
98        /// The returned pointer is valid for the lifetime of this `Token`.
99        #[inline(always)]
100        pub fn as_char_ptr(&self) -> *const c_char {
101            self.0.as_ptr()
102        }
103    }
104
105    impl From<&str> for Token {
106        #[inline(always)]
107        fn from(s: &str) -> Self {
108            Self::new(s)
109        }
110    }
111}
112
113pub use inner::Token;