use crate::c_box::CBox;
use libc::c_char;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::ffi::{CStr, CString, OsStr, OsString};
use std::iter::FusedIterator;
use std::ops::Index;
use std::os::unix::ffi::OsStrExt;
use std::{fmt, slice};
#[repr(transparent)]
#[derive(Debug)]
pub struct EnvItem(CBox<c_char>);
impl EnvItem {
#[must_use]
pub fn as_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.0.as_ref()) }
}
#[must_use]
pub fn key_value(&self) -> (&OsStr, &OsStr) {
let element = <&CStr>::from(self).to_bytes();
let sep = element
.iter()
.position(|b| *b == b'=')
.unwrap_or(element.len());
(
OsStr::from_bytes(&element[..sep]),
OsStr::from_bytes(&element[sep + 1..]),
)
}
}
impl fmt::Display for EnvItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", <&CStr>::from(self).to_string_lossy())
}
}
impl<'a> From<&'a EnvItem> for &'a CStr {
#[inline]
fn from(item: &'a EnvItem) -> Self {
item.as_cstr()
}
}
impl<'a> From<&'a EnvItem> for (&'a OsStr, &'a OsStr) {
fn from(item: &'a EnvItem) -> Self {
item.key_value()
}
}
impl AsRef<CStr> for EnvItem {
#[inline]
fn as_ref(&self) -> &CStr {
self.as_cstr()
}
}
impl PartialEq for EnvItem {
#[inline]
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(self.as_cstr(), other.as_cstr())
}
}
impl Eq for EnvItem {}
impl PartialOrd for EnvItem {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
PartialOrd::partial_cmp(self.as_cstr(), other.as_cstr())
}
}
impl Ord for EnvItem {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(self.as_cstr(), other.as_cstr())
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for EnvItem {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.key_value().serialize(serializer)
}
}
unsafe fn count_items<T: ?Sized>(mut ptr: *const *const T) -> usize {
let mut result: usize = 0;
while !(*ptr).is_null() {
ptr = ptr.add(1);
result += 1;
}
result
}
#[derive(Debug)]
pub struct EnvList(CBox<[EnvItem]>);
impl EnvList {
#[must_use]
pub(crate) unsafe fn new(data: *mut *mut c_char) -> Self {
assert!(!data.is_null());
let len = count_items(data as *const *const c_char);
Self(CBox::from_raw_slice(data.cast(), len))
}
#[must_use]
pub fn get<T: AsRef<OsStr>>(&self, name: T) -> Option<&OsStr> {
#[inline]
fn get<'a>(list: &'a EnvList, name: &'_ OsStr) -> Option<&'a OsStr> {
list.iter_tuples()
.find_map(|(k, v)| if k == name { Some(v) } else { None })
}
get(self, name.as_ref())
}
#[inline]
pub fn iter(&self) -> Iter {
self.0.iter()
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn iter_tuples(&self) -> TupleIter {
TupleIter(self.0.iter())
}
}
impl fmt::Display for EnvList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for item in self.0.iter() {
writeln!(f, "{}", item.as_cstr().to_string_lossy())?;
}
Ok(())
}
}
impl<'a> IntoIterator for &'a EnvList {
type Item = &'a EnvItem;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl AsRef<[EnvItem]> for EnvList {
#[inline]
fn as_ref(&self) -> &[EnvItem] {
&self.0
}
}
impl From<EnvList> for Vec<(OsString, OsString)> {
fn from(list: EnvList) -> Self {
let mut vec = Vec::with_capacity(list.len());
for (key, value) in list.iter_tuples() {
vec.push((key.to_owned(), value.to_owned()));
}
vec
}
}
impl<'a> From<&'a EnvList> for Vec<(&'a OsStr, &'a OsStr)> {
fn from(list: &'a EnvList) -> Self {
list.iter_tuples().collect()
}
}
impl From<EnvList> for Vec<CString> {
fn from(list: EnvList) -> Self {
let mut vec = Vec::with_capacity(list.len());
for item in list.0.iter() {
vec.push(item.as_cstr().to_owned());
}
vec
}
}
impl<'a> From<&'a EnvList> for Vec<&'a CStr> {
fn from(list: &'a EnvList) -> Self {
let mut vec = Vec::with_capacity(list.len());
for item in list.0.iter() {
vec.push(item.as_cstr());
}
vec
}
}
impl<S> From<EnvList> for HashMap<OsString, OsString, S>
where
S: ::std::hash::BuildHasher + Default,
{
fn from(list: EnvList) -> Self {
let mut map = HashMap::<_, _, S>::with_capacity_and_hasher(list.len(), S::default());
for (key, value) in list.iter_tuples() {
map.insert(key.to_owned(), value.to_owned());
}
map
}
}
impl<'a, S> From<&'a EnvList> for HashMap<&'a OsStr, &'a OsStr, S>
where
S: ::std::hash::BuildHasher + Default,
{
fn from(list: &'a EnvList) -> Self {
list.iter_tuples().collect()
}
}
impl<T: AsRef<OsStr>> Index<T> for EnvList {
type Output = OsStr;
fn index(&self, name: T) -> &Self::Output {
self.get(name).expect("environment variable not found")
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for EnvList {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
pub type Iter<'a> = slice::Iter<'a, EnvItem>;
#[must_use]
#[derive(Debug)]
pub struct TupleIter<'a>(slice::Iter<'a, EnvItem>);
impl<'a> Iterator for TupleIter<'a> {
type Item = (&'a OsStr, &'a OsStr);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(EnvItem::key_value)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl FusedIterator for TupleIter<'_> {}
impl ExactSizeIterator for TupleIter<'_> {}