#![allow(deprecated)]
use crate::boot::{self, ScopedProtocol};
use crate::proto::unsafe_protocol;
use crate::{CStr16, Error, Handle, Result, Status, StatusExt};
use core::fmt::{self, Debug, Display, Formatter};
use core::{ptr, slice};
use uefi_raw::protocol::driver::ComponentName2Protocol;
#[deprecated = "deprecated in UEFI 2.1; use ComponentName2 where possible"]
#[unsafe_protocol(ComponentName2Protocol::DEPRECATED_COMPONENT_NAME_GUID)]
#[derive(Debug)]
#[repr(transparent)]
pub struct ComponentName1(
ComponentName2Protocol,
);
impl ComponentName1 {
pub const fn supported_languages(
&self,
) -> core::result::Result<LanguageIter<'_>, LanguageError> {
LanguageIter::new(self.0.supported_languages, LanguageIterKind::V1)
}
pub fn driver_name(&self, language: &str) -> Result<&CStr16> {
let language = language_to_cstr(language)?;
let mut driver_name = ptr::null();
unsafe { (self.0.get_driver_name)(&self.0, language.as_ptr(), &mut driver_name) }
.to_result_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
}
pub fn controller_name(
&self,
controller_handle: Handle,
child_handle: Option<Handle>,
language: &str,
) -> Result<&CStr16> {
let language = language_to_cstr(language)?;
let mut driver_name = ptr::null();
unsafe {
(self.0.get_controller_name)(
&self.0,
controller_handle.as_ptr(),
Handle::opt_to_ptr(child_handle),
language.as_ptr(),
&mut driver_name,
)
}
.to_result_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
}
}
#[unsafe_protocol(ComponentName2Protocol::GUID)]
#[derive(Debug)]
#[repr(transparent)]
pub struct ComponentName2(ComponentName2Protocol);
impl ComponentName2 {
pub const fn supported_languages(
&self,
) -> core::result::Result<LanguageIter<'_>, LanguageError> {
LanguageIter::new(self.0.supported_languages, LanguageIterKind::V2)
}
pub fn driver_name(&self, language: &str) -> Result<&CStr16> {
let language = language_to_cstr(language)?;
let mut driver_name = ptr::null();
unsafe { (self.0.get_driver_name)(&self.0, language.as_ptr(), &mut driver_name) }
.to_result_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
}
pub fn controller_name(
&self,
controller_handle: Handle,
child_handle: Option<Handle>,
language: &str,
) -> Result<&CStr16> {
let language = language_to_cstr(language)?;
let mut driver_name = ptr::null();
unsafe {
(self.0.get_controller_name)(
&self.0,
controller_handle.as_ptr(),
Handle::opt_to_ptr(child_handle),
language.as_ptr(),
&mut driver_name,
)
}
.to_result_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
}
}
pub enum ComponentName {
V1(ScopedProtocol<ComponentName1>),
V2(ScopedProtocol<ComponentName2>),
}
impl ComponentName {
pub fn open(handle: Handle) -> Result<Self> {
if let Ok(cn2) = boot::open_protocol_exclusive::<ComponentName2>(handle) {
Ok(Self::V2(cn2))
} else {
Ok(Self::V1(boot::open_protocol_exclusive::<ComponentName1>(
handle,
)?))
}
}
#[allow(clippy::missing_const_for_fn)] pub fn supported_languages(&self) -> core::result::Result<LanguageIter<'_>, LanguageError> {
match self {
Self::V1(cn1) => cn1.supported_languages(),
Self::V2(cn2) => cn2.supported_languages(),
}
}
pub fn driver_name(&self, language: &str) -> Result<&CStr16> {
match self {
Self::V1(cn1) => cn1.driver_name(language),
Self::V2(cn2) => cn2.driver_name(language),
}
}
pub fn controller_name(
&self,
controller_handle: Handle,
child_handle: Option<Handle>,
language: &str,
) -> Result<&CStr16> {
match self {
Self::V1(cn1) => cn1.controller_name(controller_handle, child_handle, language),
Self::V2(cn2) => cn2.controller_name(controller_handle, child_handle, language),
}
}
}
impl Debug for ComponentName {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::V1(_) => f.debug_tuple("V1").finish(),
Self::V2(_) => f.debug_tuple("V2").finish(),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum LanguageError {
Ascii {
index: usize,
},
}
impl Display for LanguageError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Ascii { index } => write!(f, "invalid character at index: {index}"),
}
}
}
impl core::error::Error for LanguageError {}
#[derive(Debug, PartialEq)]
enum LanguageIterKind {
V1,
V2,
}
#[derive(Debug)]
pub struct LanguageIter<'a> {
languages: &'a [u8],
kind: LanguageIterKind,
}
impl LanguageIter<'_> {
const fn new(
languages: *const u8,
kind: LanguageIterKind,
) -> core::result::Result<Self, LanguageError> {
let mut index = 0;
loop {
let c = unsafe { languages.add(index).read() };
if c == 0 {
break;
} else if !c.is_ascii() {
return Err(LanguageError::Ascii { index });
} else {
index += 1;
}
}
Ok(Self {
languages: unsafe { slice::from_raw_parts(languages, index) },
kind,
})
}
}
impl<'a> Iterator for LanguageIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
if self.languages.is_empty() {
return None;
}
let lang;
match self.kind {
LanguageIterKind::V1 => {
if self.languages.len() <= 3 {
lang = self.languages;
self.languages = &[];
} else {
lang = &self.languages[..3];
self.languages = &self.languages[3..];
}
}
LanguageIterKind::V2 => {
if let Some(index) = self.languages.iter().position(|c| *c == b';') {
lang = &self.languages[..index];
self.languages = &self.languages[index + 1..];
} else {
lang = self.languages;
self.languages = &[];
}
}
}
Some(core::str::from_utf8(lang).unwrap())
}
}
type LanguageCStr = [u8; 64];
fn language_to_cstr(language: &str) -> Result<LanguageCStr> {
let mut lang_cstr: LanguageCStr = [0; 64];
if language.len() >= lang_cstr.len() - 1 {
return Err(Error::from(Status::BUFFER_TOO_SMALL));
}
lang_cstr[..language.len()].copy_from_slice(language.as_bytes());
assert_eq!(*lang_cstr.last().unwrap(), 0);
Ok(lang_cstr)
}
#[cfg(test)]
mod tests {
use super::*;
use LanguageIterKind::{V1, V2};
use alloc::vec::Vec;
#[test]
fn test_language_iter_v1() {
let data = "\0";
assert!(
LanguageIter::new(data.as_ptr(), V1)
.unwrap()
.next()
.is_none()
);
let data = "engfra\0";
assert_eq!(
LanguageIter::new(data.as_ptr(), V1)
.unwrap()
.collect::<Vec<_>>(),
["eng", "fra"]
);
let data = "en\0";
assert_eq!(
LanguageIter::new(data.as_ptr(), V1)
.unwrap()
.collect::<Vec<_>>(),
["en"]
);
let data = "engæ\0";
assert_eq!(
LanguageIter::new(data.as_ptr(), V1).err().unwrap(),
LanguageError::Ascii { index: 3 },
);
}
#[test]
fn test_language_iter_v2() {
let data = "\0";
assert!(
LanguageIter::new(data.as_ptr(), V2)
.unwrap()
.next()
.is_none()
);
let data = "en;fr\0";
assert_eq!(
LanguageIter::new(data.as_ptr(), V2)
.unwrap()
.collect::<Vec<_>>(),
["en", "fr"]
);
let data = "engæ\0";
assert_eq!(
LanguageIter::new(data.as_ptr(), V2).err().unwrap(),
LanguageError::Ascii { index: 3 },
);
}
#[test]
fn test_language_to_cstr() {
let mut expected = [0; 64];
expected[0] = b'e';
expected[1] = b'n';
assert_eq!(language_to_cstr("en"), Ok(expected));
assert_eq!(
language_to_cstr(
"0123456789012345678901234567890123456789012345678901234567890123456789"
)
.err()
.unwrap()
.status(),
Status::BUFFER_TOO_SMALL
);
}
}