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