use crate::{
component::{
Component,
metadata::MetaData,
params::{ComponentInput, Param, ParamFunction},
storage::{Storage, UnsafeStorageCell},
},
error::Result,
};
use core::marker::PhantomData;
pub struct StructComponent<Marker, Func>
where
Func: ParamFunction<Marker>,
{
func: Func,
input: Option<Func::In>,
param_state: Option<<Func::Param as Param>::State>,
metadata: MetaData,
_marker: PhantomData<fn() -> Marker>,
}
impl<Marker, Func> StructComponent<Marker, Func>
where
Marker: 'static,
Func: ParamFunction<Marker>,
{
pub fn new(func: Func, input: Func::In) -> Self {
Self {
func,
input: Some(input),
param_state: None,
metadata: MetaData::new::<Func::In>(),
_marker: PhantomData,
}
}
}
impl<Marker, In, Func> Component for StructComponent<Marker, Func>
where
Marker: 'static,
In: ComponentInput + 'static,
Func: ParamFunction<Marker, In = In, Out = Result<()>>,
{
unsafe fn run_unsafe(&mut self, storage: UnsafeStorageCell) -> Result<bool> {
let param_state = self.param_state.as_mut().expect("Param state created on initialize.");
if let Err(bad_param) = Func::Param::try_validate(param_state, storage) {
self.metadata.set_error_message(bad_param);
return Ok(false);
}
let param_value = unsafe { Func::Param::get_param(param_state, storage) };
debug_assert!(
self.input.is_some(),
"{} `input` is `None` during run. Did this component already run?",
super::type_name::normalized::<Self>()
);
log::info!("Dispatching {}", self.metadata.name());
self.func.run(&mut self.input, param_value).map(|_| true)
}
fn metadata(&self) -> &MetaData {
&self.metadata
}
fn initialize(&mut self, _storage: &mut Storage) -> bool {
match Func::Param::init_state(_storage, &mut self.metadata) {
Ok(param_state) => {
self.param_state = Some(param_state);
true
}
Err(init_error) => {
self.metadata.set_error_message(init_error);
false
}
}
}
}
#[cfg(test)]
#[coverage(off)]
mod tests {
use crate as patina;
use crate::component::{
IntoComponent, component,
params::{Config, ConfigMut},
};
use alloc::borrow::Cow;
#[allow(dead_code)]
pub struct TestStructSuccess {
pub x: i32,
}
#[component]
impl TestStructSuccess {
fn entry_point(self, _cfg: crate::component::params::Config<i32>) -> crate::error::Result<()> {
Ok(())
}
}
#[allow(dead_code)]
pub enum TestEnumSuccess {
A,
B,
}
#[component]
impl TestEnumSuccess {
fn entry_point(self, _cfg: Config<i32>) -> crate::error::Result<()> {
Ok(())
}
}
#[allow(dead_code)]
pub struct TestStructNotDispatched {
pub x: i32,
}
#[component]
impl TestStructNotDispatched {
fn entry_point(self, _cfg: ConfigMut<u32>) -> crate::error::Result<()> {
Ok(())
}
}
#[allow(dead_code)]
pub struct TestStructFail {
pub x: i32,
}
#[component]
impl TestStructFail {
fn entry_point(self) -> crate::error::Result<()> {
Err(crate::error::EfiError::NotReady)
}
}
#[test]
fn test_struct_component() {
let test_struct = TestStructSuccess { x: 5 };
let _ = test_struct.into_component();
}
#[test]
fn test_enum_component() {
let test_enum = TestEnumSuccess::A;
let _ = test_enum.into_component();
}
#[test]
fn test_component_run_handling_works_as_expected() {
let mut storage = crate::component::storage::Storage::new();
let mut test_struct = TestStructSuccess { x: 5 }.into_component();
test_struct.initialize(&mut storage);
assert!(test_struct.run(&mut storage).is_ok_and(|res| res));
let mut test_enum = TestEnumSuccess::A.into_component();
test_enum.initialize(&mut storage);
assert!(test_enum.run(&mut storage).is_ok_and(|res| res));
let mut test_struct = TestStructNotDispatched { x: 5 }.into_component();
test_struct.initialize(&mut storage);
storage.lock_configs(); assert!(test_struct.run(&mut storage).is_ok_and(|res| !res));
assert_eq!(
test_struct.metadata().error_message(),
Some(Cow::from("patina::component::params::ConfigMut<u32>"))
);
let mut test_struct = TestStructFail { x: 5 }.into_component();
test_struct.initialize(&mut storage);
assert!(test_struct.run(&mut storage).is_err_and(|res| res == crate::error::EfiError::NotReady));
}
struct GenericStruct<T>
where
T: 'static,
{
_x: T,
}
#[component]
impl<T> GenericStruct<T> {
fn entry_point(self, _cfg: Config<u32>) -> crate::error::Result<()> {
Ok(())
}
}
#[test]
fn test_generic_struct_can_be_component() {
let test_struct = GenericStruct { _x: 5 };
let _ = test_struct.into_component();
}
struct GenericStruct2<T: 'static> {
_x: T,
}
#[component]
impl<T: 'static> GenericStruct2<T> {
fn entry_point(self, _cfg: Config<u32>) -> crate::error::Result<()> {
Ok(())
}
}
#[test]
fn test_generic_struct_with_where_clause_can_be_component() {
let test_struct = GenericStruct2 { _x: 5 };
let _ = test_struct.into_component();
}
#[test]
fn test_component_entry_point_that_take_by_value_works() {
struct ByValue {
_x: u32,
}
#[component]
impl ByValue {
fn entry_point(self, _cfg: Config<u32>) -> crate::error::Result<()> {
Ok(())
}
}
let _ = ByValue { _x: 5 }.into_component();
}
#[test]
fn test_component_entry_point_that_take_by_ref_works() {
struct ByRef {
_x: u32,
}
#[component]
impl ByRef {
fn entry_point(&self, _cfg: Config<u32>) -> crate::error::Result<()> {
Ok(())
}
}
let _ = ByRef { _x: 5 }.into_component();
}
#[test]
fn test_component_entry_point_that_take_by_mut_works() {
struct ByMut {
_x: u32,
}
#[component]
impl ByMut {
fn entry_point(&mut self, _cfg: Config<u32>) -> crate::error::Result<()> {
Ok(())
}
}
let _ = ByMut { _x: 5 }.into_component();
}
}