use crate::data_types::{CStr8, CStr16};
use crate::proto::unsafe_protocol;
use core::cmp::Ordering;
use core::fmt::{self, Display, Formatter};
use uefi_raw::protocol::string::UnicodeCollationProtocol;
#[derive(Debug)]
#[repr(transparent)]
#[unsafe_protocol(UnicodeCollationProtocol::GUID)]
pub struct UnicodeCollation(UnicodeCollationProtocol);
impl UnicodeCollation {
#[must_use]
pub fn stri_coll(&self, s1: &CStr16, s2: &CStr16) -> Ordering {
let order = unsafe { (self.0.stri_coll)(&self.0, s1.as_ptr().cast(), s2.as_ptr().cast()) };
order.cmp(&0)
}
#[must_use]
pub fn metai_match(&self, s: &CStr16, pattern: &CStr16) -> bool {
unsafe { (self.0.metai_match)(&self.0, s.as_ptr().cast(), pattern.as_ptr().cast()) }.into()
}
pub fn str_lwr<'a>(
&self,
s: &CStr16,
buf: &'a mut [u16],
) -> Result<&'a CStr16, StrConversionError> {
let mut last_index = 0;
for (i, c) in s.iter().enumerate() {
*buf.get_mut(i).ok_or(StrConversionError::BufferTooSmall)? = (*c).into();
last_index = i;
}
*buf.get_mut(last_index + 1)
.ok_or(StrConversionError::BufferTooSmall)? = 0;
unsafe { (self.0.str_lwr)(&self.0, buf.as_mut_ptr()) };
Ok(unsafe { CStr16::from_u16_with_nul_unchecked(buf) })
}
pub fn str_upr<'a>(
&self,
s: &CStr16,
buf: &'a mut [u16],
) -> Result<&'a CStr16, StrConversionError> {
let mut last_index = 0;
for (i, c) in s.iter().enumerate() {
*buf.get_mut(i).ok_or(StrConversionError::BufferTooSmall)? = (*c).into();
last_index = i;
}
*buf.get_mut(last_index + 1)
.ok_or(StrConversionError::BufferTooSmall)? = 0;
unsafe { (self.0.str_upr)(&self.0, buf.as_mut_ptr()) };
Ok(unsafe { CStr16::from_u16_with_nul_unchecked(buf) })
}
pub fn fat_to_str<'a>(
&self,
fat: &CStr8,
buf: &'a mut [u16],
) -> Result<&'a CStr16, StrConversionError> {
if buf.len() < fat.as_bytes().len() {
return Err(StrConversionError::BufferTooSmall);
}
unsafe {
(self.0.fat_to_str)(
&self.0,
fat.as_bytes().len(),
fat.as_ptr().cast(),
buf.as_mut_ptr(),
)
};
Ok(unsafe { CStr16::from_u16_with_nul_unchecked(buf) })
}
pub fn str_to_fat<'a>(
&self,
s: &CStr16,
buf: &'a mut [u8],
) -> Result<&'a CStr8, StrConversionError> {
if s.as_slice_with_nul().len() > buf.len() {
return Err(StrConversionError::BufferTooSmall);
}
let failed = unsafe {
(self.0.str_to_fat)(
&self.0,
s.as_ptr().cast(),
s.as_slice_with_nul().len(),
buf.as_mut_ptr(),
)
};
if bool::from(failed) {
Err(StrConversionError::ConversionFailed)
} else {
let mut last_null_index = buf.len() - 1;
for i in (0..buf.len()).rev() {
if buf[i] != 0 {
last_null_index = i + 1;
break;
}
}
let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr(), last_null_index + 1) };
Ok(unsafe { CStr8::from_bytes_with_nul_unchecked(buf) })
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StrConversionError {
ConversionFailed,
BufferTooSmall,
}
impl Display for StrConversionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::ConversionFailed => "conversion failed",
Self::BufferTooSmall => "buffer too small",
}
)
}
}
impl core::error::Error for StrConversionError {}