cstring_array/traits.rs
1// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
2//
3// SPDX-License-Identifier: MIT
4
5//! Trait implementations for CStringArray.
6//!
7//! This module provides ergonomic conversions from various string collection
8//! types into `CStringArray` through the `TryFrom` trait. These implementations
9//! allow for flexible and convenient array construction from different input
10//! formats.
11
12use std::ffi::CString;
13
14use crate::{array::CStringArray, error::CStringArrayError};
15
16impl TryFrom<Vec<String>> for CStringArray {
17 type Error = CStringArrayError;
18
19 /// Converts a `Vec<String>` into a `CStringArray`.
20 ///
21 /// # Errors
22 ///
23 /// Returns an error if any string contains an interior null byte or if the
24 /// vector is empty.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// use std::convert::TryFrom;
30 ///
31 /// use cstring_array::CStringArray;
32 ///
33 /// let strings = vec!["hello".to_string(), "world".to_string()];
34 /// let array = CStringArray::try_from(strings).unwrap();
35 /// assert_eq!(array.len(), 2);
36 /// ```
37 fn try_from(strings: Vec<String>) -> Result<Self, Self::Error> {
38 CStringArray::new(strings)
39 }
40}
41
42impl TryFrom<Vec<&str>> for CStringArray {
43 type Error = CStringArrayError;
44
45 /// Converts a `Vec<&str>` into a `CStringArray`.
46 ///
47 /// # Errors
48 ///
49 /// Returns an error if any string contains an interior null byte or if the
50 /// vector is empty.
51 ///
52 /// # Example
53 ///
54 /// ```
55 /// use std::convert::TryFrom;
56 ///
57 /// use cstring_array::CStringArray;
58 ///
59 /// let strings = vec!["hello", "world"];
60 /// let array = CStringArray::try_from(strings).unwrap();
61 /// assert_eq!(array.len(), 2);
62 /// ```
63 fn try_from(strings: Vec<&str>) -> Result<Self, Self::Error> {
64 let owned: Vec<String> = strings.into_iter().map(String::from).collect();
65 CStringArray::new(owned)
66 }
67}
68
69impl<const N: usize> TryFrom<[String; N]> for CStringArray {
70 type Error = CStringArrayError;
71
72 /// Converts an array of `String`s into a `CStringArray`.
73 ///
74 /// # Errors
75 ///
76 /// Returns an error if any string contains an interior null byte or if the
77 /// array is empty.
78 ///
79 /// # Example
80 ///
81 /// ```
82 /// use std::convert::TryFrom;
83 ///
84 /// use cstring_array::CStringArray;
85 ///
86 /// let strings = ["hello".to_string(), "world".to_string()];
87 /// let array = CStringArray::try_from(strings).unwrap();
88 /// assert_eq!(array.len(), 2);
89 /// ```
90 fn try_from(strings: [String; N]) -> Result<Self, Self::Error> {
91 CStringArray::new(strings.to_vec())
92 }
93}
94
95impl<const N: usize> TryFrom<[&str; N]> for CStringArray {
96 type Error = CStringArrayError;
97
98 /// Converts an array of string slices into a `CStringArray`.
99 ///
100 /// # Errors
101 ///
102 /// Returns an error if any string contains an interior null byte or if the
103 /// array is empty.
104 ///
105 /// # Example
106 ///
107 /// ```
108 /// use std::convert::TryFrom;
109 ///
110 /// use cstring_array::CStringArray;
111 ///
112 /// let strings = ["hello", "world"];
113 /// let array = CStringArray::try_from(strings).unwrap();
114 /// assert_eq!(array.len(), 2);
115 /// ```
116 fn try_from(strings: [&str; N]) -> Result<Self, Self::Error> {
117 let owned: Vec<String> = strings.into_iter().map(String::from).collect();
118 CStringArray::new(owned)
119 }
120}
121
122impl TryFrom<Vec<CString>> for CStringArray {
123 type Error = CStringArrayError;
124
125 /// Converts a `Vec<CString>` into a `CStringArray` (zero-copy).
126 ///
127 /// # Errors
128 ///
129 /// Returns an error if the vector is empty.
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// use std::{convert::TryFrom, ffi::CString};
135 ///
136 /// use cstring_array::CStringArray;
137 ///
138 /// let cstrings = vec![
139 /// CString::new("hello").unwrap(),
140 /// CString::new("world").unwrap(),
141 /// ];
142 /// let array = CStringArray::try_from(cstrings).unwrap();
143 /// assert_eq!(array.len(), 2);
144 /// ```
145 fn try_from(strings: Vec<CString>) -> Result<Self, Self::Error> {
146 CStringArray::from_cstrings(strings)
147 }
148}
149
150// ============================================================================
151// Comparison Traits
152// ============================================================================
153
154impl PartialEq for CStringArray {
155 fn eq(&self, other: &Self) -> bool {
156 self.as_slice() == other.as_slice()
157 }
158}
159
160impl Eq for CStringArray {}
161
162impl std::hash::Hash for CStringArray {
163 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
164 self.as_slice().hash(state);
165 }
166}
167
168// ============================================================================
169// Clone Trait
170// ============================================================================
171
172impl Clone for CStringArray {
173 fn clone(&self) -> Self {
174 Self::from_cstrings(self.as_slice().to_vec()).expect("clone from non-empty array")
175 }
176}
177
178// ============================================================================
179// Iterator Traits
180// ============================================================================
181
182impl FromIterator<String> for CStringArray {
183 fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
184 let strings: Vec<String> = iter.into_iter().collect();
185 Self::new(strings).expect("FromIterator from non-empty iterator")
186 }
187}
188
189impl FromIterator<CString> for CStringArray {
190 fn from_iter<I: IntoIterator<Item = CString>>(iter: I) -> Self {
191 let strings: Vec<CString> = iter.into_iter().collect();
192 Self::from_cstrings(strings).expect("FromIterator from non-empty iterator")
193 }
194}
195
196impl IntoIterator for CStringArray {
197 type Item = CString;
198 type IntoIter = std::vec::IntoIter<CString>;
199
200 fn into_iter(self) -> Self::IntoIter {
201 self.into_strings().into_iter()
202 }
203}
204
205impl<'a> IntoIterator for &'a CStringArray {
206 type Item = &'a CString;
207 type IntoIter = std::slice::Iter<'a, CString>;
208
209 fn into_iter(self) -> Self::IntoIter {
210 self.iter()
211 }
212}
213
214// ============================================================================
215// Indexing Traits
216// ============================================================================
217
218impl std::ops::Index<usize> for CStringArray {
219 type Output = CString;
220
221 fn index(&self, index: usize) -> &Self::Output {
222 &self.as_slice()[index]
223 }
224}
225
226// ============================================================================
227// Conversion Traits
228// ============================================================================
229
230impl AsRef<[CString]> for CStringArray {
231 fn as_ref(&self) -> &[CString] {
232 self.as_slice()
233 }
234}