1use crate::ffi::*;
35use std::{ffi::CStr, fmt, mem::MaybeUninit};
36
37pub type StrVector = igraph_strvector_t;
39
40impl igraph_strvector_t {
41 pub fn new() -> Self {
44 crate::error::ensure_init();
45 let mut raw = MaybeUninit::<Self>::uninit();
46 crate::error::check(unsafe { igraph_strvector_init(raw.as_mut_ptr(), 0) })
47 .expect("igraph failed to allocate a string vector");
48 unsafe { raw.assume_init() }
49 }
50
51 pub fn len(&self) -> usize {
54 unsafe { igraph_strvector_size(self) as usize }
55 }
56
57 pub fn is_empty(&self) -> bool {
59 self.len() == 0
60 }
61
62 pub fn get(&self, index: usize) -> Option<&str> {
67 self.get_cstr(index).and_then(|s| s.to_str().ok())
68 }
69
70 pub fn get_cstr(&self, index: usize) -> Option<&CStr> {
73 if index >= self.len() {
74 return None;
75 }
76 let ptr = unsafe { igraph_strvector_get(self, index as igraph_int_t) };
77 (!ptr.is_null()).then(|| unsafe { CStr::from_ptr(ptr) })
78 }
79
80 pub fn push(&mut self, value: &str) {
83 crate::error::check(unsafe {
84 igraph_strvector_push_back_len(self, value.as_ptr().cast(), value.len())
85 })
86 .expect("igraph failed to grow a string vector");
87 }
88
89 pub fn iter(&self) -> impl Iterator<Item = String> + '_ {
91 (0..self.len()).map(move |i| {
92 self.get_cstr(i)
93 .map(|s| s.to_string_lossy().into_owned())
94 .unwrap_or_default()
95 })
96 }
97
98 pub fn to_vec(&self) -> Vec<String> {
100 self.iter().collect()
101 }
102}
103
104impl Drop for igraph_strvector_t {
105 fn drop(&mut self) {
106 if !self.stor_begin.is_null() {
107 unsafe { igraph_strvector_destroy(self) };
108 self.stor_begin = std::ptr::null_mut();
109 }
110 }
111}
112
113impl Default for igraph_strvector_t {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119impl Clone for igraph_strvector_t {
120 fn clone(&self) -> Self {
121 crate::error::ensure_init();
122 let mut raw = MaybeUninit::<Self>::uninit();
123 crate::error::check(unsafe { igraph_strvector_init_copy(raw.as_mut_ptr(), self) })
124 .expect("igraph failed to copy a string vector");
125 unsafe { raw.assume_init() }
126 }
127}
128
129impl PartialEq for igraph_strvector_t {
130 fn eq(&self, other: &Self) -> bool {
131 self.len() == other.len() && (0..self.len()).all(|i| self.get_cstr(i) == other.get_cstr(i))
132 }
133}
134
135impl fmt::Display for igraph_strvector_t {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.debug_list().entries(self.iter()).finish()
138 }
139}
140
141impl<S: AsRef<str>> FromIterator<S> for igraph_strvector_t {
142 fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
143 let mut sv = Self::new();
144 for s in iter {
145 sv.push(s.as_ref());
146 }
147 sv
148 }
149}
150
151impl<S: AsRef<str>> From<&[S]> for igraph_strvector_t {
152 fn from(items: &[S]) -> Self {
153 items.iter().collect()
154 }
155}
156
157impl From<&igraph_strvector_t> for Vec<String> {
158 fn from(sv: &igraph_strvector_t) -> Self {
159 sv.to_vec()
160 }
161}
162
163unsafe impl Send for igraph_strvector_t {}
164unsafe impl Sync for igraph_strvector_t {}
165
166impl igraph_strvector_t {
167 pub fn with_len(len: usize) -> Self {
170 crate::error::ensure_init();
171 let mut raw = MaybeUninit::<Self>::uninit();
172 crate::error::check(unsafe {
173 igraph_strvector_init(raw.as_mut_ptr(), crate::error::int_size(len))
174 })
175 .expect("igraph failed to allocate a string vector");
176 unsafe { raw.assume_init() }
177 }
178
179 pub fn set(&mut self, index: usize, value: &str) -> crate::error::Result<()> {
195 if index >= self.len() {
196 return Err(crate::error::Error::invalid(format!(
197 "string index {index} out of bounds (len {})",
198 self.len()
199 )));
200 }
201 crate::igraph_call!(igraph_strvector_set_len(
202 self,
203 index as igraph_int_t,
204 value.as_ptr().cast(),
205 value.len()
206 ))
207 }
208
209 pub fn remove(&mut self, index: usize) -> String {
215 let len = self.len();
216 assert!(
217 index < len,
218 "string index {index} out of bounds (len {len})"
219 );
220 let value = self
221 .get_cstr(index)
222 .map(|s| s.to_string_lossy().into_owned())
223 .unwrap_or_default();
224 unsafe { igraph_strvector_remove(self, index as igraph_int_t) };
225 value
226 }
227
228 pub fn pop(&mut self) -> Option<String> {
230 let len = self.len();
231 (len > 0).then(|| self.remove(len - 1))
232 }
233
234 pub fn remove_section(&mut self, range: std::ops::Range<usize>) {
240 let len = self.len();
241 assert!(
242 range.start <= range.end && range.end <= len,
243 "section {range:?} out of bounds (len {len})"
244 );
245 unsafe {
246 igraph_strvector_remove_section(
247 self,
248 range.start as igraph_int_t,
249 range.end as igraph_int_t,
250 )
251 };
252 }
253
254 pub fn extend_from(&mut self, other: &Self) {
257 crate::error::check(unsafe { igraph_strvector_append(self, other) })
258 .expect("igraph failed to grow a string vector");
259 }
260
261 pub fn append(&mut self, other: &mut Self) {
265 crate::error::check(unsafe { igraph_strvector_merge(self, other) })
266 .expect("igraph failed to grow a string vector");
267 }
268
269 pub fn resize(&mut self, len: usize) {
272 crate::error::check(unsafe { igraph_strvector_resize(self, crate::error::int_size(len)) })
273 .expect("igraph failed to resize a string vector");
274 }
275
276 pub fn truncate(&mut self, len: usize) {
278 if len < self.len() {
279 self.resize(len);
280 }
281 }
282
283 pub fn clear(&mut self) {
286 unsafe { igraph_strvector_clear(self) }
287 }
288
289 pub fn select(&self, index: &[igraph_int_t]) -> crate::error::Result<Self> {
296 let len = self.len();
297 if let Some(i) = index.iter().find(|&&i| i < 0 || i as usize >= len) {
298 return Err(crate::error::Error::invalid(format!(
299 "string index {i} out of bounds (len {len})"
300 )));
301 }
302 let idx = crate::vector::VectorInt::view(index);
303 let mut res = Self::new();
304 crate::igraph_call!(igraph_strvector_index(self, &mut res, idx.as_ptr()))?;
305 Ok(res)
306 }
307
308 pub fn swap(&mut self, i: usize, j: usize) {
314 let len = self.len();
315 assert!(
316 i < len && j < len,
317 "string indices ({i}, {j}) out of bounds (len {len})"
318 );
319 unsafe { igraph_strvector_swap_elements(self, i as igraph_int_t, j as igraph_int_t) }
320 }
321
322 pub fn capacity(&self) -> usize {
325 unsafe { igraph_strvector_capacity(self) as usize }
326 }
327
328 pub fn reserve(&mut self, capacity: usize) {
331 crate::error::check(unsafe {
332 igraph_strvector_reserve(self, crate::error::int_size(capacity))
333 })
334 .expect("igraph failed to reserve string vector storage");
335 }
336
337 pub fn shrink_to_fit(&mut self) {
340 unsafe { igraph_strvector_resize_min(self) }
341 }
342
343 pub fn contains(&self, value: &str) -> bool {
345 self.position(value).is_some()
346 }
347
348 pub fn position(&self, value: &str) -> Option<usize> {
350 (0..self.len()).find(|&i| {
351 self.get_cstr(i)
352 .is_some_and(|s| s.to_bytes() == value.as_bytes())
353 })
354 }
355}
356
357impl std::ops::Index<usize> for igraph_strvector_t {
358 type Output = str;
359 fn index(&self, index: usize) -> &str {
364 let len = self.len();
365 assert!(
366 index < len,
367 "string index {index} out of bounds (len {len})"
368 );
369 self.get(index).expect("the string is not valid UTF-8")
370 }
371}
372
373impl<S: AsRef<str>> Extend<S> for igraph_strvector_t {
374 fn extend<I: IntoIterator<Item = S>>(&mut self, iter: I) {
375 for s in iter {
376 self.push(s.as_ref());
377 }
378 }
379}
380
381impl From<Vec<String>> for igraph_strvector_t {
382 fn from(items: Vec<String>) -> Self {
383 items.iter().collect()
384 }
385}
386
387impl From<igraph_strvector_t> for Vec<String> {
388 fn from(sv: igraph_strvector_t) -> Self {
389 sv.to_vec()
390 }
391}
392
393impl<'a> IntoIterator for &'a igraph_strvector_t {
394 type Item = String;
395 type IntoIter = Box<dyn Iterator<Item = String> + 'a>;
396 fn into_iter(self) -> Self::IntoIter {
397 Box::new(self.iter())
398 }
399}
400
401impl PartialEq<[&str]> for igraph_strvector_t {
402 fn eq(&self, other: &[&str]) -> bool {
403 self.len() == other.len()
404 && other.iter().enumerate().all(|(i, s)| {
405 self.get_cstr(i)
406 .is_some_and(|c| c.to_bytes() == s.as_bytes())
407 })
408 }
409}