use core::mem;
use alloc::{vec, vec::Vec};
use fallible_streaming_iterator::FallibleStreamingIterator;
use r_efi::efi::{self, Guid};
use super::RuntimeServices;
#[derive(Debug)]
pub enum GetVariableStatus {
Error(efi::Status),
BufferTooSmall {
data_size: usize,
attributes: u32,
},
Success {
data_size: usize,
attributes: u32,
},
}
#[derive(Debug)]
pub struct VariableInfo {
pub maximum_variable_storage_size: u64,
pub remaining_variable_storage_size: u64,
pub maximum_variable_size: u64,
}
#[derive(Debug)]
pub struct VariableIdentifier {
name: Vec<u16>,
namespace: efi::Guid,
}
#[derive(Debug)]
pub struct VariableNameIterator<'a, R: RuntimeServices> {
rs: &'a R,
current: VariableIdentifier,
next: VariableIdentifier,
finished: bool,
}
impl<'a, R: RuntimeServices> VariableNameIterator<'a, R> {
pub fn new_from_first(runtime_services: &'a R) -> Self {
Self {
rs: runtime_services,
current: VariableIdentifier {
name: {
let prev_name = vec![0; 1];
prev_name
},
namespace: Guid::from_bytes(&[0x0; 16]),
},
next: VariableIdentifier { name: Vec::<u16>::new(), namespace: Guid::from_bytes(&[0x0; 16]) },
finished: false,
}
}
pub fn new_from_variable(name: &[u16], namespace: &efi::Guid, runtime_services: &'a R) -> Self {
Self {
rs: runtime_services,
current: VariableIdentifier { name: name.to_vec(), namespace: *namespace },
next: VariableIdentifier { name: Vec::<u16>::new(), namespace: Guid::from_bytes(&[0x0; 16]) },
finished: false,
}
}
}
impl<R: RuntimeServices> FallibleStreamingIterator for VariableNameIterator<'_, R> {
type Item = VariableIdentifier;
type Error = efi::Status;
fn advance(&mut self) -> Result<(), Self::Error> {
unsafe {
if self.finished {
return Ok(());
}
let status = self.rs.get_next_variable_name_unchecked(
&self.current.name,
&self.current.namespace,
&mut self.next.name,
&mut self.next.namespace,
);
mem::swap(&mut self.current, &mut self.next);
if status.is_err() && status.unwrap_err() == efi::Status::NOT_FOUND {
self.finished = true;
Ok(())
} else {
status
}
}
}
fn get(&self) -> Option<&Self::Item> {
if self.finished { None } else { Some(&self.current) }
}
}
#[cfg(test)]
#[coverage(off)]
mod test {
use r_efi::efi;
use super::*;
use crate::runtime_services::{
StandardRuntimeServices,
test::{
DUMMY_FIRST_NAME, DUMMY_FIRST_NAMESPACE, DUMMY_SECOND_NAME, DUMMY_SECOND_NAMESPACE,
mock_efi_get_next_variable_name, runtime_services,
},
};
use std::mem;
#[test]
fn test_variable_name_iterator_from_first() {
let rs = runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
let mut iter = VariableNameIterator::new_from_first(&rs);
let mut status = iter.next();
assert!(status.is_ok());
assert!(status.unwrap().is_some());
let mut variable_identifier = status.unwrap().unwrap();
assert_eq!(variable_identifier.name, DUMMY_FIRST_NAME);
assert_eq!(variable_identifier.namespace, DUMMY_FIRST_NAMESPACE);
status = iter.next();
assert!(status.is_ok());
assert!(status.unwrap().is_some());
variable_identifier = status.unwrap().unwrap();
assert_eq!(variable_identifier.name, DUMMY_SECOND_NAME);
assert_eq!(variable_identifier.namespace, DUMMY_SECOND_NAMESPACE);
status = iter.next();
assert!(status.is_ok());
assert!(status.unwrap().is_none());
}
#[test]
fn test_variable_name_iterator_from_second() {
let rs = runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
let mut iter = VariableNameIterator::new_from_variable(&DUMMY_FIRST_NAME, &DUMMY_FIRST_NAMESPACE, &rs);
let mut status = iter.next();
assert!(status.is_ok());
assert!(status.unwrap().is_some());
let variable_identifier = status.unwrap().unwrap();
assert_eq!(variable_identifier.name, DUMMY_SECOND_NAME);
assert_eq!(variable_identifier.namespace, DUMMY_SECOND_NAMESPACE);
status = iter.next();
assert!(status.is_ok());
assert!(status.unwrap().is_none());
}
}