nv_redfish/task_service/mod.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//! Task Service entities and helpers.
17//!
18//! This module provides typed access to Redfish `TaskService`.
19//! A `TaskService` value is a lightweight handle to the service schema and BMC
20//! transport. It validates task locations returned by asynchronous operations
21//! against this service's Tasks collection and returns lazy task links that can
22//! be fetched when polling is needed.
23
24use std::sync::Arc;
25
26use crate::Error;
27use crate::NvBmc;
28use crate::Resource;
29use crate::ResourceSchema;
30use crate::ServiceRoot;
31use crate::core::Bmc;
32use crate::core::EntityTypeRef as _;
33use crate::core::NavProperty;
34use crate::entity_link::EntityLink;
35use crate::schema::task::Task as TaskSchema;
36use crate::schema::task_service::TaskService as TaskServiceSchema;
37
38use nv_redfish_core::AsyncTask;
39
40/// Link to a Redfish Task returned by an asynchronous operation.
41pub type TaskLink<B> = EntityLink<B, TaskSchema>;
42
43/// Task service.
44///
45/// Provides task links for task locations returned by asynchronous operations.
46///
47/// # Example
48///
49/// ```ignore
50/// let Some(task_service) = root.task_service().await? else {
51/// return Ok(());
52/// };
53///
54/// let task_link = task_service.task_link(async_task)?;
55/// let task = task_link.fetch().await?;
56///
57/// println!("{:?}", task.task_state);
58/// ```
59pub struct TaskService<B: Bmc> {
60 data: Arc<TaskServiceSchema>,
61 bmc: NvBmc<B>,
62}
63
64impl<B: Bmc> TaskService<B> {
65 /// Create a new task service handle.
66 pub(crate) async fn new(
67 bmc: &NvBmc<B>,
68 root: &ServiceRoot<B>,
69 ) -> Result<Option<Self>, Error<B>> {
70 let Some(service_ref) = &root.root.tasks else {
71 return Ok(None);
72 };
73
74 let data = service_ref.get(bmc.as_ref()).await.map_err(Error::Bmc)?;
75
76 // Task links need the BMC-advertised Tasks collection as the allowed
77 // parent path for all async task locations.
78 if data.tasks.is_none() {
79 return Err(Error::TaskServiceTasksUnavailable);
80 }
81
82 Ok(Some(Self {
83 data,
84 bmc: bmc.clone(),
85 }))
86 }
87
88 /// Get the raw schema data for this task service.
89 #[must_use]
90 pub fn raw(&self) -> Arc<TaskServiceSchema> {
91 self.data.clone()
92 }
93
94 /// Create a task link from an asynchronous operation result.
95 ///
96 /// The task location must be a child of this service's Tasks collection,
97 /// such as `/redfish/v1/TaskService/Tasks/{id}`. The returned link does not
98 /// fetch the task until [`TaskLink::fetch`] is called.
99 ///
100 /// # Errors
101 ///
102 /// Returns error if the task location is not a child of this service's Tasks
103 /// collection.
104 pub fn task_link(&self, task: AsyncTask) -> Result<TaskLink<B>, Error<B>> {
105 let Some(tasks) = self.data.tasks.as_ref() else {
106 return Err(Error::TaskServiceTasksUnavailable);
107 };
108
109 let task_collection = tasks.odata_id();
110 let task_location = task.location.0;
111 if task_collection == &task_location || !task_collection.is_path_prefix(&task_location) {
112 return Err(Error::TaskLocationNotInTaskService {
113 task_location,
114 task_collection: task_collection.clone(),
115 });
116 }
117
118 let task_ref = NavProperty::new_reference(task_location);
119 Ok(TaskLink::new(&self.bmc, task_ref))
120 }
121}
122
123impl<B: Bmc> Resource for TaskService<B> {
124 fn resource_ref(&self) -> &ResourceSchema {
125 &self.data.as_ref().base
126 }
127}