Skip to main content

nv_redfish/
entity_link.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Generic lightweight entity link.
17//!
18//! [`EntityLink<B, T>`](crate::entity_link::EntityLink) is an owned handle that pairs a BMC client with a
19//! navigation property. It provides lazy access to any Redfish entity
20//! without eagerly fetching it.
21//!
22//! Capabilities are determined by trait bounds on the schema type `T`:
23//! - [`fetch`](crate::entity_link::EntityLink::fetch) — always available (requires `T: EntityTypeRef + Deserialize + Send + Sync`)
24//! - [`delete`](crate::entity_link::EntityLink::delete) — available when `T: Deletable`
25//!
26//! Concrete link types are defined as type aliases:
27//! ```ignore
28//! pub type SensorLink<B> = EntityLink<B, SchemaSensor>;
29//! pub type MetricReportLink<B> = EntityLink<B, MetricReportSchema>;
30//! ```
31
32use crate::Error;
33use crate::NvBmc;
34use nv_redfish_core::Bmc;
35use nv_redfish_core::Deletable;
36use nv_redfish_core::EntityTypeRef;
37use nv_redfish_core::ModificationResponse;
38use nv_redfish_core::NavProperty;
39use nv_redfish_core::ODataId;
40use serde::Deserialize;
41use std::future::Future;
42use std::sync::Arc;
43
44/// Lightweight owned handle to a Redfish entity.
45///
46/// Holds a cloned BMC client and a [`NavProperty<T>`] without eagerly
47/// fetching the entity. Use [`fetch`](Self::fetch) to retrieve the
48/// data on demand.
49///
50/// Operations like [`delete`](Self::delete) are available when the
51/// schema type `T` implements the corresponding core trait.
52pub struct EntityLink<B: Bmc, T: EntityTypeRef> {
53    bmc: NvBmc<B>,
54    nav: NavProperty<T>,
55}
56
57impl<B: Bmc, T: EntityTypeRef> EntityLink<B, T> {
58    /// Create a new entity link.
59    pub(crate) fn new(bmc: &NvBmc<B>, nav: NavProperty<T>) -> Self {
60        Self {
61            bmc: bmc.clone(),
62            nav,
63        }
64    }
65
66    /// `OData` identifier of this entity.
67    ///
68    /// Always available without network I/O.
69    #[must_use]
70    pub fn odata_id(&self) -> &ODataId {
71        self.nav.id()
72    }
73
74    /// Access the underlying navigation property.
75    #[must_use]
76    pub const fn nav(&self) -> &NavProperty<T> {
77        &self.nav
78    }
79}
80
81impl<B, T> EntityLink<B, T>
82where
83    B: Bmc,
84    T: EntityTypeRef + for<'de> Deserialize<'de> + 'static,
85{
86    /// Fetch the entity from the BMC.
87    ///
88    /// If the navigation property is already expanded, returns the
89    /// cached value without network I/O.
90    ///
91    /// # Errors
92    ///
93    /// Returns an error if fetching the entity fails.
94    pub async fn fetch(&self) -> Result<Arc<T>, Error<B>> {
95        self.nav.get(self.bmc.as_ref()).await.map_err(Error::Bmc)
96    }
97
98    /// Construct a full wrapper from this link.
99    ///
100    /// Fetches the entity and wraps it in a higher-level type
101    /// that provides richer API access.
102    ///
103    /// # Errors
104    ///
105    /// Returns an error if fetching the entity fails.
106    pub async fn upgrade<W>(&self) -> Result<W, Error<B>>
107    where
108        W: FromLink<B, Schema = T>,
109    {
110        W::from_link(&self.bmc, &self.nav).await
111    }
112}
113
114impl<B, T> EntityLink<B, T>
115where
116    B: Bmc,
117    T: Deletable,
118{
119    /// Delete this entity.
120    ///
121    /// Only available when the schema type implements [`Deletable`].
122    ///
123    /// # Errors
124    ///
125    /// Returns an error if deleting the entity fails.
126    pub async fn delete(&self) -> Result<ModificationResponse<T>, Error<B>> {
127        self.bmc
128            .as_ref()
129            .delete(self.odata_id())
130            .await
131            .map_err(Error::Bmc)
132    }
133}
134
135/// Trait for full wrapper types that can be constructed from an entity link.
136///
137/// Implemented by wrapper types that need only `NvBmc<B>` and
138/// `NavProperty<T>` to be constructed (i.e., no extra context).
139pub trait FromLink<B: Bmc>: Sized {
140    /// The schema type this wrapper is built from.
141    type Schema: EntityTypeRef + for<'de> Deserialize<'de>;
142
143    /// Construct the full wrapper by fetching entity data.
144    fn from_link(
145        bmc: &NvBmc<B>,
146        nav: &NavProperty<Self::Schema>,
147    ) -> impl Future<Output = Result<Self, Error<B>>> + Send;
148}