use crate::patch_support::FilterFn;
use crate::patch_support::JsonValue;
use crate::patch_support::Payload;
use crate::patch_support::ReadPatchFn;
use crate::schema::resource::ItemOrCollection;
use crate::schema::resource::Oem;
use crate::schema::resource::ResourceCollection;
use crate::Error;
use crate::NvBmc;
use nv_redfish_core::Bmc;
use nv_redfish_core::EntityTypeRef;
use nv_redfish_core::Expandable;
use nv_redfish_core::NavProperty;
use nv_redfish_core::ODataETag;
use nv_redfish_core::ODataId;
use serde::Deserialize;
use std::sync::Arc;
#[cfg(feature = "patch-collection-create")]
use nv_redfish_core::Creatable;
#[cfg(feature = "patch-collection-create")]
use nv_redfish_core::ModificationResponse;
#[cfg(feature = "patch-collection-create")]
use serde::Serialize;
pub trait CollectionWithPatch<T, M, B>
where
T: EntityTypeRef + Expandable + Send + Sync + 'static,
M: EntityTypeRef + Send + Sync + for<'de> Deserialize<'de>,
B: Bmc,
{
fn convert_patched(base: ResourceCollection, members: Vec<NavProperty<M>>) -> T;
async fn expand_collection(
bmc: &NvBmc<B>,
nav: &NavProperty<T>,
patch_fn: Option<&ReadPatchFn>,
filter_fn: Option<&FilterFn>,
) -> Result<Arc<T>, Error<B>> {
if patch_fn.is_some() || filter_fn.is_some() {
let patched_collection_ref = NavProperty::<Collection>::new_reference(nav.id().clone());
let collection = bmc.expand_property(&patched_collection_ref).await?;
let patch_fn = patch_fn.map(AsRef::as_ref);
let filter_fn = filter_fn.map(AsRef::as_ref);
let members = collection.members(patch_fn, filter_fn)?;
Ok(Arc::new(Self::convert_patched(collection.base(), members)))
} else {
bmc.expand_property(nav).await
}
}
}
#[cfg(feature = "patch-collection-create")]
pub trait CreateWithPatch<T, M, C, B>
where
T: EntityTypeRef + Creatable<C, M> + Sync + Send,
C: Serialize + Sync + Send,
M: for<'de> Deserialize<'de> + Sync + Send,
B: Bmc,
{
fn entity_ref(&self) -> &T;
fn patch(&self) -> Option<&ReadPatchFn>;
fn bmc(&self) -> &B;
async fn create_with_patch(&self, create: &C) -> Result<ModificationResponse<M>, Error<B>> {
if let Some(patch_fn) = &self.patch() {
Collection::create(self.entity_ref(), self.bmc(), create, patch_fn.as_ref()).await
} else {
self.entity_ref()
.create(self.bmc(), create)
.await
.map_err(Error::Bmc)
}
}
}
#[derive(Deserialize)]
struct Collection {
#[serde(flatten)]
base: ResourceCollection,
#[serde(rename = "Members")]
members: Vec<Payload>,
}
impl Collection {
#[cfg(feature = "patch-collection-create")]
async fn create<T, F, C, B, V>(
orig: &T,
bmc: &B,
create: &C,
f: F,
) -> Result<ModificationResponse<V>, Error<B>>
where
T: EntityTypeRef + Sync + Send,
V: for<'de> Deserialize<'de>,
B: Bmc,
C: Serialize + Sync + Send,
F: Fn(JsonValue) -> JsonValue + Sync + Send,
{
let result = Creator {
id: orig.odata_id(),
}
.create(bmc, create)
.await
.map_err(Error::Bmc)?;
match result {
ModificationResponse::Entity(payload) => payload
.to_target::<V, B, _>(&f)
.map(ModificationResponse::Entity),
ModificationResponse::Task(task) => Ok(ModificationResponse::Task(task)),
ModificationResponse::Empty => Ok(ModificationResponse::Empty),
}
}
fn base(&self) -> ResourceCollection {
ResourceCollection {
base: ItemOrCollection {
odata_id: self.base.base.odata_id.clone(),
odata_etag: self.base.base.odata_etag.clone(),
redfish_settings: None,
redfish_settings_apply_type: None,
},
odata_type: self.base.odata_type.clone(),
description: self.base.description.clone(),
name: self.base.name.clone(),
oem: self.base.oem.as_ref().map(|oem| Oem {
additional_properties: oem.additional_properties.clone(),
}),
}
}
fn members<T, FP, FF, B>(
&self,
patch_fn: Option<&FP>,
filter_fn: Option<&FF>,
) -> Result<Vec<NavProperty<T>>, Error<B>>
where
T: EntityTypeRef + for<'de> Deserialize<'de>,
FP: Fn(JsonValue) -> JsonValue + ?Sized,
FF: Fn(&JsonValue) -> bool + ?Sized,
B: Bmc,
{
self.members
.iter()
.filter(|v| filter_fn.is_none_or(|ff| v.filter(ff)))
.map(|v| patch_fn.map_or_else(|| v.parse(), |fp| v.to_target(fp)))
.collect::<Result<Vec<_>, _>>()
}
}
impl EntityTypeRef for Collection {
fn odata_id(&self) -> &ODataId {
self.base.odata_id()
}
fn etag(&self) -> Option<&ODataETag> {
self.base.etag()
}
}
impl Expandable for Collection {}
#[cfg(feature = "patch-collection-create")]
struct Creator<'a> {
id: &'a ODataId,
}
#[cfg(feature = "patch-collection-create")]
impl EntityTypeRef for Creator<'_> {
fn odata_id(&self) -> &ODataId {
self.id
}
fn etag(&self) -> Option<&ODataETag> {
None
}
}
#[cfg(feature = "patch-collection-create")]
impl<V: Serialize + Send + Sync> Creatable<V, Payload> for Creator<'_> {}