1use crate::c_box::CBox;
12use libc::c_char;
13use std::cmp::Ordering;
14use std::collections::HashMap;
15use std::ffi::{CStr, CString, OsStr, OsString};
16use std::iter::FusedIterator;
17use std::ops::Index;
18use std::os::unix::ffi::OsStrExt;
19use std::{fmt, slice};
20
21#[repr(transparent)]
29#[derive(Debug)]
30pub struct EnvItem(CBox<c_char>);
31
32impl EnvItem {
33 #[must_use]
35 pub fn as_cstr(&self) -> &CStr {
36 unsafe { CStr::from_ptr(self.0.as_ref()) }
37 }
38
39 #[must_use]
41 pub fn key_value(&self) -> (&OsStr, &OsStr) {
42 let element = <&CStr>::from(self).to_bytes();
43 let sep = element
44 .iter()
45 .position(|b| *b == b'=')
46 .unwrap_or(element.len());
47 (
48 OsStr::from_bytes(&element[..sep]),
49 OsStr::from_bytes(&element[sep + 1..]),
50 )
51 }
52}
53
54impl fmt::Display for EnvItem {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "{}", <&CStr>::from(self).to_string_lossy())
60 }
61}
62
63impl<'a> From<&'a EnvItem> for &'a CStr {
64 #[inline]
65 fn from(item: &'a EnvItem) -> Self {
66 item.as_cstr()
67 }
68}
69
70impl<'a> From<&'a EnvItem> for (&'a OsStr, &'a OsStr) {
71 fn from(item: &'a EnvItem) -> Self {
72 item.key_value()
73 }
74}
75
76impl AsRef<CStr> for EnvItem {
77 #[inline]
78 fn as_ref(&self) -> &CStr {
79 self.as_cstr()
80 }
81}
82
83impl PartialEq for EnvItem {
84 #[inline]
85 fn eq(&self, other: &Self) -> bool {
86 PartialEq::eq(self.as_cstr(), other.as_cstr())
87 }
88}
89
90impl Eq for EnvItem {}
91
92impl PartialOrd for EnvItem {
93 #[inline]
94 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
95 PartialOrd::partial_cmp(self.as_cstr(), other.as_cstr())
96 }
97}
98
99impl Ord for EnvItem {
100 #[inline]
101 fn cmp(&self, other: &Self) -> Ordering {
102 Ord::cmp(self.as_cstr(), other.as_cstr())
103 }
104}
105
106#[cfg(feature = "serde")]
108impl serde::Serialize for EnvItem {
109 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
110 self.key_value().serialize(serializer)
111 }
112}
113
114unsafe fn count_items<T: ?Sized>(mut ptr: *const *const T) -> usize {
116 let mut result: usize = 0;
117 while !(*ptr).is_null() {
118 ptr = ptr.add(1);
119 result += 1;
120 }
121 result
122}
123
124#[derive(Debug)]
179pub struct EnvList(CBox<[EnvItem]>);
180
181impl EnvList {
182 #[must_use]
188 pub(crate) unsafe fn new(data: *mut *mut c_char) -> Self {
189 assert!(!data.is_null());
190 let len = count_items(data as *const *const c_char);
191 Self(CBox::from_raw_slice(data.cast(), len))
192 }
193
194 #[must_use]
198 pub fn get<T: AsRef<OsStr>>(&self, name: T) -> Option<&OsStr> {
199 #[inline]
200 fn get<'a>(list: &'a EnvList, name: &'_ OsStr) -> Option<&'a OsStr> {
201 list.iter_tuples()
202 .find_map(|(k, v)| if k == name { Some(v) } else { None })
203 }
204 get(self, name.as_ref())
205 }
206
207 #[inline]
211 pub fn iter(&self) -> Iter {
212 self.0.iter()
213 }
214
215 #[inline]
217 #[must_use]
218 pub fn len(&self) -> usize {
219 self.0.len()
220 }
221
222 #[inline]
224 #[must_use]
225 pub fn is_empty(&self) -> bool {
226 self.0.is_empty()
227 }
228
229 #[inline]
236 pub fn iter_tuples(&self) -> TupleIter {
237 TupleIter(self.0.iter())
238 }
239}
240
241impl fmt::Display for EnvList {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 for item in self.0.iter() {
248 writeln!(f, "{}", item.as_cstr().to_string_lossy())?;
249 }
250 Ok(())
251 }
252}
253
254impl<'a> IntoIterator for &'a EnvList {
258 type Item = &'a EnvItem;
259 type IntoIter = Iter<'a>;
260
261 fn into_iter(self) -> Self::IntoIter {
262 self.iter()
263 }
264}
265
266impl AsRef<[EnvItem]> for EnvList {
268 #[inline]
269 fn as_ref(&self) -> &[EnvItem] {
270 &self.0
271 }
272}
273
274impl From<EnvList> for Vec<(OsString, OsString)> {
276 fn from(list: EnvList) -> Self {
277 let mut vec = Vec::with_capacity(list.len());
278 for (key, value) in list.iter_tuples() {
279 vec.push((key.to_owned(), value.to_owned()));
280 }
281 vec
282 }
283}
284
285impl<'a> From<&'a EnvList> for Vec<(&'a OsStr, &'a OsStr)> {
287 fn from(list: &'a EnvList) -> Self {
288 list.iter_tuples().collect()
289 }
290}
291
292impl From<EnvList> for Vec<CString> {
294 fn from(list: EnvList) -> Self {
295 let mut vec = Vec::with_capacity(list.len());
296 for item in list.0.iter() {
297 vec.push(item.as_cstr().to_owned());
298 }
299 vec
300 }
301}
302
303impl<'a> From<&'a EnvList> for Vec<&'a CStr> {
305 fn from(list: &'a EnvList) -> Self {
306 let mut vec = Vec::with_capacity(list.len());
307 for item in list.0.iter() {
308 vec.push(item.as_cstr());
309 }
310 vec
311 }
312}
313
314impl<S> From<EnvList> for HashMap<OsString, OsString, S>
316where
317 S: ::std::hash::BuildHasher + Default,
318{
319 fn from(list: EnvList) -> Self {
320 let mut map = HashMap::<_, _, S>::with_capacity_and_hasher(list.len(), S::default());
321 for (key, value) in list.iter_tuples() {
322 map.insert(key.to_owned(), value.to_owned());
323 }
324 map
325 }
326}
327
328impl<'a, S> From<&'a EnvList> for HashMap<&'a OsStr, &'a OsStr, S>
330where
331 S: ::std::hash::BuildHasher + Default,
332{
333 fn from(list: &'a EnvList) -> Self {
334 list.iter_tuples().collect()
335 }
336}
337
338impl<T: AsRef<OsStr>> Index<T> for EnvList {
340 type Output = OsStr;
341
342 fn index(&self, name: T) -> &Self::Output {
347 self.get(name).expect("environment variable not found")
348 }
349}
350
351#[cfg(feature = "serde")]
353impl serde::Serialize for EnvList {
354 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
355 self.0.serialize(serializer)
356 }
357}
358
359pub type Iter<'a> = slice::Iter<'a, EnvItem>;
361
362#[must_use]
367#[derive(Debug)]
368pub struct TupleIter<'a>(slice::Iter<'a, EnvItem>);
369
370impl<'a> Iterator for TupleIter<'a> {
371 type Item = (&'a OsStr, &'a OsStr);
372
373 fn next(&mut self) -> Option<Self::Item> {
374 self.0.next().map(EnvItem::key_value)
375 }
376
377 #[inline]
378 fn size_hint(&self) -> (usize, Option<usize>) {
379 self.0.size_hint()
380 }
381}
382
383impl FusedIterator for TupleIter<'_> {}
384impl ExactSizeIterator for TupleIter<'_> {}