1use std::ffi::{CStr, CString};
3use std::fmt::Formatter;
4
5use crate::errors::Result;
6use crate::session::Session;
7
8#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13#[repr(transparent)]
14pub struct StringHandle(pub(crate) i32);
15
16#[derive(Clone, PartialEq)]
20pub struct StringArray(pub Vec<u8>);
21
22impl std::fmt::Debug for StringArray {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 if f.alternate() {
25 let strings = self.iter_str().collect::<Vec<_>>();
26 strings.fmt(f)
27 } else {
28 let count = bytecount::count(self.0.as_slice(), b'\0');
29 write!(f, "StringArray[num_strings = {count}]")
30 }
31 }
32}
33
34pub struct StringIter<'a> {
36 inner: &'a [u8],
37}
38
39pub struct OwnedStringIter {
41 inner: Vec<u8>,
42 cursor: usize,
43}
44
45impl Iterator for OwnedStringIter {
46 type Item = String;
47
48 fn next(&mut self) -> Option<Self::Item> {
49 match self
50 .inner
51 .iter()
52 .skip(self.cursor)
53 .position(|b| *b == b'\0')
54 {
55 None => None,
56 Some(pos) => {
57 let idx = self.cursor + pos;
58 let ret = &self.inner[self.cursor..idx];
59 self.cursor = idx + 1;
60 Some(String::from_utf8_lossy(ret).to_string())
61 }
62 }
63 }
64}
65
66pub struct CStringIter<'a> {
68 inner: &'a [u8],
69}
70
71impl<'a> StringArray {
72 #[must_use]
74 pub fn empty() -> StringArray {
75 StringArray(vec![])
76 }
77 #[must_use]
79 pub fn iter_str(&'a self) -> StringIter<'a> {
80 StringIter { inner: &self.0 }
81 }
82
83 #[must_use]
85 pub fn iter_cstr(&'a self) -> CStringIter<'a> {
86 CStringIter { inner: &self.0 }
87 }
88
89 #[inline]
90 #[must_use]
91 pub fn is_empty(&self) -> bool {
92 self.0.is_empty()
93 }
94
95 #[inline]
97 #[must_use]
98 pub fn bytes(&self) -> &[u8] {
99 self.0.as_slice()
100 }
101}
102
103impl From<StringArray> for Vec<String> {
104 fn from(a: StringArray) -> Self {
105 a.into_iter().collect()
106 }
107}
108
109impl<'a> Iterator for StringIter<'a> {
110 type Item = &'a str;
111
112 fn next(&mut self) -> Option<Self::Item> {
113 match self.inner.iter().position(|c| *c == b'\0') {
114 None => None,
115 Some(idx) => {
116 let ret = &self.inner[..idx];
117 self.inner = &self.inner[idx + 1..];
118 unsafe { Some(std::str::from_utf8_unchecked(ret)) }
119 }
120 }
121 }
122}
123
124impl<'a> Iterator for CStringIter<'a> {
125 type Item = &'a CStr;
126
127 fn next(&mut self) -> Option<Self::Item> {
128 match self.inner.iter().position(|c| *c == b'\0') {
129 None => None,
130 Some(idx) => {
131 let ret = &self.inner[..=idx];
132 self.inner = &self.inner[idx + 1..];
133 unsafe { Some(CStr::from_bytes_with_nul_unchecked(ret)) }
134 }
135 }
136 }
137}
138
139impl IntoIterator for StringArray {
140 type Item = String;
141 type IntoIter = OwnedStringIter;
142
143 fn into_iter(self) -> Self::IntoIter {
144 OwnedStringIter {
145 inner: self.0,
146 cursor: 0,
147 }
148 }
149}
150
151pub(crate) fn get_string(handle: StringHandle, session: &Session) -> Result<String> {
152 let bytes = crate::ffi::get_string_bytes(session, handle)?;
153 String::from_utf8(bytes).map_err(crate::HapiError::from)
154}
155
156pub(crate) fn get_cstring(handle: StringHandle, session: &Session) -> Result<CString> {
157 let bytes = crate::ffi::get_string_bytes(session, handle)?;
158 CString::new(bytes).map_err(crate::HapiError::from)
159}
160
161pub fn get_string_array(handles: &[StringHandle], session: &Session) -> Result<StringArray> {
162 let _lock = session.lock();
163 let length = crate::ffi::get_string_batch_size(handles, session)?;
164 let bytes = if length > 0 {
165 crate::ffi::get_string_batch_bytes(length, session)?
166 } else {
167 vec![]
168 };
169 Ok(StringArray(bytes))
170}
171
172#[cfg(test)]
173mod tests {
174 use super::StringArray;
175
176 #[test]
177 fn string_array_empty_and_bytes() {
178 let arr = StringArray::empty();
179 assert!(arr.is_empty());
180 assert!(arr.bytes().is_empty());
181 }
182
183 #[test]
184 fn string_array_debug() {
185 let arr = StringArray(b"One\0Two\0".to_vec());
186 assert_eq!(format!("{arr:?}"), "StringArray[num_strings = 2]");
187 let alt = format!("{arr:#?}");
188 assert!(alt.contains("One") && alt.contains("Two"));
189 }
190
191 #[test]
192 fn string_array_into_vec_string() {
193 let arr = StringArray(b"One\0Two\0Three\0".to_vec());
194 let v: Vec<String> = arr.into();
195 assert_eq!(v, vec!["One", "Two", "Three"]);
196 }
197
198 #[test]
199 fn string_array_iter_cstr() {
200 let arr = StringArray(b"One\0Two\0Three\0".to_vec());
201 let v: Vec<_> = arr.iter_cstr().collect();
202 assert_eq!(v[0].to_bytes_with_nul(), b"One\0");
203 assert_eq!(v[2].to_bytes_with_nul(), b"Three\0");
204 }
205}