Skip to main content

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;