use core::marker::PhantomData; use yggdrasil_rt::path::Path; use crate::{ error::{EResult, OptionExt}, path::{PathBuf, PathExt}, ENVS, }; pub trait Nullable { fn is_null(&self) -> bool; } pub struct NullTerminated<'a, T: Nullable> { data: *const T, _pd: PhantomData<&'a ()>, } impl Nullable for *const T { fn is_null(&self) -> bool { <*const T>::is_null(*self) } } impl Nullable for *mut T { fn is_null(&self) -> bool { <*mut T>::is_null(*self) } } impl<'a, T: Nullable> NullTerminated<'a, T> { pub unsafe fn try_from_ptr(ptr: *const T) -> Option { (!ptr.is_null()).then(|| Self { data: ptr, _pd: PhantomData, }) } } impl<'a, T: Nullable + 'a> Iterator for NullTerminated<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { let value = unsafe { &*self.data }; if value.is_null() { return None; } self.data = unsafe { self.data.add(1) }; Some(value) } } pub fn envs() -> impl Iterator { // SAFETY C_ENVS is only mutable during init let it = unsafe { ENVS.iter() }; it.filter_map(|v| v.to_str().ok().and_then(|s| s.split_once('='))) } pub fn getenv(name: &str) -> Option<&'static str> { envs().find_map(|(k, v)| (k == name).then_some(v)) } pub fn resolve_binary>(name: P) -> EResult { let name = name.as_ref(); if name.is_absolute() { return EResult::Ok(PathBuf::from(name)); } let env_path = getenv("PATH").e_ok_or(yggdrasil_rt::Error::DoesNotExist)?; for el in env_path.split(':') { let path = PathBuf::from_str(el).join(name); if path.exists() { return EResult::Ok(path); } } EResult::Err(yggdrasil_rt::Error::DoesNotExist) }