Skip to main content

nv_redfish_core/
action.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025 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//! Redfish Action primitives
17//!
18//! The [`Action<T, R>`] type corresponds to the inner object found under the
19//! Redfish `Actions` section for a specific action (for example,
20//! `"#ComputerSystem.Reset"`). It captures the endpoint used to invoke the
21//! action via its `target` field. The type parameters are:
22//! - `T`: request parameters payload type (sent as the POST body when running the action)
23//! - `R`: response type returned by the BMC for that action
24//!
25//! Only the `target` field is deserialized. Any additional metadata
26//! (such as `...@Redfish.AllowableValues`) is ignored by this type
27//! and may be used by higher layers.
28//!
29//! Example: how an action appears in a Redfish resource and which part maps to [`Action`]
30//!
31//! ```json
32//! {
33//!   "Actions": {
34//!     "#ComputerSystem.Reset": {
35//!       "target": "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset",
36//!       "ResetType@Redfish.AllowableValues": [
37//!         "On",
38//!         "GracefulRestart",
39//!         "ForceRestart"
40//!       ]
41//!     }
42//!   }
43//! }
44//! ```
45//!
46//! The [`Action<T, R>`] value corresponds to the inner object of
47//! `"#ComputerSystem.Reset"` and deserializes the `target` field only.
48//!
49
50use crate::Bmc;
51use crate::ModificationResponse;
52use core::fmt::Debug;
53use core::fmt::Display;
54use core::fmt::Formatter;
55use core::fmt::Result as FmtResult;
56use serde::Deserialize;
57use serde::Serialize;
58use std::marker::PhantomData;
59
60/// URI reference for the `target` field of an action.
61///
62/// The [`Bmc`] implementation resolves this value when the action is run and
63/// may reject values that violate its outbound request policy before transport.
64#[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
65#[repr(transparent)]
66pub struct ActionTarget(String);
67
68impl ActionTarget {
69    /// Creates new `ActionTarget`.
70    #[must_use]
71    pub const fn new(v: String) -> Self {
72        Self(v)
73    }
74
75    /// Returns the action target URI reference.
76    #[must_use]
77    pub fn as_str(&self) -> &str {
78        &self.0
79    }
80}
81
82impl Display for ActionTarget {
83    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
84        Display::fmt(&self.0, f)
85    }
86}
87
88/// Defines a deserializable Action. It is almost always a member of the
89/// `Actions` struct in different parts of the Redfish object tree.
90///
91/// `T` is the type for parameters.
92/// `R` is the type for the return value.
93#[derive(Deserialize)]
94pub struct Action<T, R> {
95    /// URI reference used to trigger the action.
96    #[serde(rename = "target")]
97    pub target: ActionTarget,
98    // TODO: we can retrieve constraints on attributes here.
99    /// Establishes a dependency on the `T` (parameters) type.
100    #[serde(skip_deserializing)]
101    _marker: PhantomData<T>,
102    /// Establishes a dependency on the `R` (return value) type.
103    #[serde(skip_deserializing)]
104    _marker_retval: PhantomData<R>,
105}
106
107impl<T, R> Debug for Action<T, R> {
108    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
109        f.debug_struct("Action")
110            .field("target", &self.target)
111            .finish()
112    }
113}
114
115/// Action error trait. Needed in generated code when an action function
116/// is called for an action that wasn't specified by the server.
117pub trait ActionError {
118    /// Create an error when the action is not supported.
119    fn not_supported() -> Self;
120}
121
122impl<T: Send + Sync + Serialize, R: Send + Sync + Sized + for<'de> Deserialize<'de>> Action<T, R> {
123    /// Run specific action with parameters passed as argument.
124    ///
125    /// URI-reference resolution and outbound request policy are handled by
126    /// [`Bmc::action`].
127    ///
128    /// # Errors
129    ///
130    /// Returns an error if the [`Bmc`] implementation rejects the action
131    /// request or if the Redfish service returns an error.
132    pub async fn run<B: Bmc>(
133        &self,
134        bmc: &B,
135        params: &T,
136    ) -> Result<ModificationResponse<R>, B::Error> {
137        bmc.action::<T, R>(self, params).await
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::Action;
144    use super::ActionTarget;
145    use std::marker::PhantomData;
146
147    struct NotDebug;
148
149    #[test]
150    fn debug_does_not_require_parameter_or_result_debug() {
151        let action: Action<NotDebug, NotDebug> = Action {
152            target: ActionTarget::new("/redfish/v1/Actions/Test".into()),
153            _marker: PhantomData,
154            _marker_retval: PhantomData,
155        };
156
157        assert_eq!(
158            format!("{action:?}"),
159            "Action { target: ActionTarget(\"/redfish/v1/Actions/Test\") }"
160        );
161    }
162}