nv-redfish-core 0.8.1

Semantic-unaware foundation used by code generated from CSDL for nv-redfish
Documentation
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Core Redfish foundation library used by code generated from the CSDL compiler.
//!
//! Purpose
//! - Serve as a dependency for autogenerated Redfish types produced by the CSDL compiler.
//! - Provide semantic-unaware primitives that generated code relies on.
//! - Avoid any knowledge of specific Redfish services, schemas, or OEM semantics.
//!
//! Scope (building blocks only)
//! - Identity and metadata: [`ODataId`], [`ODataETag`]
//! - EDM value wrappers: [`EdmDateTimeOffset`], [`EdmDuration`]
//! - Navigation properties: [`NavProperty<T>`]
//! - Generic operation traits: [`Creatable`], [`Updatable`], [`Deletable`]
//! - Entity contracts: [`EntityTypeRef`], [`Expandable`]
//! - Action envelope: [`Action<T, R>`]
//! - Client abstraction: [`Bmc`] (transport-agnostic interface used by generated code)
//!
//! Non-goals
//! - No service- or schema-specific models are defined here.
//! - No business logic or policy decisions are embedded here.
//! - No transport specification
//!
//! How generated code uses these primitives
//! - Each generated entity struct implements [`EntityTypeRef`].
//! - Navigation properties in generated code are wrapped in [`NavProperty<T>`].
//! - Generated actions are represented as [`Action<T, R>`].
//! - If the schema allows it, generated types implement [`Creatable`], [`Updatable`], and/or [`Deletable`]
//!   and route operations through a user-provided [`Bmc`] implementation.
//!

#![deny(
    clippy::all,
    clippy::pedantic,
    clippy::nursery,
    clippy::suspicious,
    clippy::complexity,
    clippy::perf
)]
#![deny(
    clippy::absolute_paths,
    clippy::todo,
    clippy::unimplemented,
    clippy::tests_outside_test_module,
    clippy::panic,
    clippy::unwrap_used,
    clippy::unwrap_in_result,
    clippy::unused_trait_names,
    clippy::print_stdout,
    clippy::print_stderr
)]
#![deny(missing_docs)]

/// Action-related types.
pub mod action;
/// BMC trait and credentials.
pub mod bmc;
/// Custom deserialization helpers.
pub mod deserialize;
/// Dynamic properties support.
pub mod dynamic_properties;
/// `Edm.DateTimeOffset` type.
pub mod edm_date_time_offset;
/// `Edm.Duration` type.
pub mod edm_duration;
/// `Edm.PrimitiveType` type.
pub mod edm_primitive_type;
/// Navigation property wrapper.
pub mod nav_property;
/// Type for `@odata.id` identifier.
pub mod odata;
/// Support of redfish queries
pub mod query;

use crate::query::ExpandQuery;
use futures_core::TryStream;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::pin::Pin;
use std::{future::Future, sync::Arc};

#[doc(inline)]
pub use action::Action;
#[doc(inline)]
pub use action::ActionError;
#[doc(inline)]
pub use bmc::Bmc;
#[doc(inline)]
pub use deserialize::de_optional_nullable;
#[doc(inline)]
pub use deserialize::de_required_nullable;
#[doc(inline)]
pub use dynamic_properties::DynamicProperties;
#[doc(inline)]
pub use edm_date_time_offset::EdmDateTimeOffset;
#[doc(inline)]
pub use edm_duration::EdmDuration;
#[doc(inline)]
pub use edm_primitive_type::EdmPrimitiveType;
#[doc(inline)]
pub use nav_property::NavProperty;
#[doc(inline)]
pub use nav_property::Reference;
#[doc(inline)]
pub use nav_property::ReferenceLeaf;
#[doc(inline)]
pub use odata::ODataETag;
#[doc(inline)]
pub use odata::ODataId;
#[doc(inline)]
pub use query::FilterQuery;
#[doc(inline)]
pub use query::ToFilterLiteral;
#[doc(inline)]
pub use serde_json::Value as AdditionalProperties;
#[doc(inline)]
pub use uuid::Uuid as EdmGuid;

/// Entity type reference trait implemented by the CSDL compiler
/// for all generated entity types and for all [`NavProperty<T>`] where
/// `T` is a struct for an entity type.
pub trait EntityTypeRef: Send + Sync + Sized {
    /// Value of `@odata.id` field of the Entity.
    fn odata_id(&self) -> &ODataId;

    /// Value of `@odata.etag` field of the Entity.
    fn etag(&self) -> Option<&ODataETag>;

    /// Refresh the entity by fetching it again from the BMC.
    fn refresh<B: Bmc>(&self, bmc: &B) -> impl Future<Output = Result<Arc<Self>, B::Error>> + Send
    where
        Self: for<'de> Deserialize<'de> + 'static,
    {
        bmc.get::<Self>(self.odata_id())
    }
}

/// Defines entity types that support `$expand` via query parameters.
pub trait Expandable: EntityTypeRef + for<'de> Deserialize<'de> + 'static {
    /// Expand the entity according to the provided query.
    fn expand<B: Bmc>(
        &self,
        bmc: &B,
        query: ExpandQuery,
    ) -> impl Future<Output = Result<Arc<Self>, B::Error>> + Send {
        bmc.expand::<Self>(self.odata_id(), query)
    }
}

/// Boxed fallible stream used by BMC streaming APIs.
pub type BoxTryStream<T, E> =
    Pin<Box<dyn TryStream<Ok = T, Error = E, Item = Result<T, E>> + Send>>;

/// Outcome of a mutating Redfish operation that can complete asynchronously.
#[derive(Debug)]
pub struct AsyncTask {
    /// Request completed successfully with no response body
    pub id: ODataId,
    /// The recommended number of seconds to wait before polling again
    pub retry_after_secs: Option<u64>,
}

/// Outcome of a mutating Redfish operation.
#[derive(Debug)]
pub enum ModificationResponse<T> {
    /// Request completed synchronously
    Entity(T),
    /// Requst completed asynchronously, with the provided `ODataId` to poll for completion
    Task(AsyncTask),
    /// Request completed successfully with no response body
    Empty,
}

/// Redfish session creation returns the session resource in the response body,
/// the authentication token in the `X-Auth-Token` header, and the session URI in
/// the `Location` header.
pub struct SessionCreateResponse<T> {
    /// Created session entity.
    pub entity: T,
    /// Authentication token from `X-Auth-Token`.
    pub auth_token: String,
    /// Session resource URI from `Location`.
    pub location: ODataId,
}

impl<T: fmt::Debug> fmt::Debug for SessionCreateResponse<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SessionCreateResponse")
            .field("entity", &self.entity)
            .field("auth_token", &"[REDACTED]")
            .field("location", &self.location)
            .finish()
    }
}

/// This trait is assigned to the collections that are marked as
/// creatable in the CSDL specification.
pub trait Creatable<V: Send + Sync + Serialize, R: Send + Sync + for<'de> Deserialize<'de>>:
    EntityTypeRef
{
    /// Create an entity using `create` as payload.
    fn create<B: Bmc>(
        &self,
        bmc: &B,
        create: &V,
    ) -> impl Future<Output = Result<ModificationResponse<R>, B::Error>> + Send {
        bmc.create::<V, R>(self.odata_id(), create)
    }
}

/// This trait is assigned to entity types that are marked as
/// updatable in the CSDL specification.
pub trait Updatable<V: Sync + Send + Serialize>: EntityTypeRef + for<'de> Deserialize<'de> {
    /// Update an entity using `update` as payload.
    fn update<B: Bmc>(
        &self,
        bmc: &B,
        update: &V,
    ) -> impl Future<Output = Result<ModificationResponse<Self>, B::Error>> + Send {
        bmc.update::<V, Self>(self.odata_id(), self.etag(), update)
    }
}

/// This trait is assigned to entity types that are marked as
/// deletable in the CSDL specification.
pub trait Deletable: EntityTypeRef + for<'de> Deserialize<'de> {
    /// Delete current entity.
    fn delete<B: Bmc>(
        &self,
        bmc: &B,
    ) -> impl Future<Output = Result<ModificationResponse<Self>, B::Error>> + Send {
        bmc.delete::<Self>(self.odata_id())
    }
}

/// This trait is assigned to updatable entity types to support
/// @Redfish.Settings workflow.
pub trait RedfishSettings<E: EntityTypeRef>: Sized {
    /// Reference to the enity type object.
    fn settings_object(&self) -> Option<NavProperty<E>>;
}

/// Trait for converting enum variants to `snake_case` strings
pub trait ToSnakeCase {
    /// Convert this enum variant to a `snake_case` string
    fn to_snake_case(&self) -> &'static str;
}

/// Trait for types that can be used as filter properties in `OData` queries
pub trait FilterProperty {
    /// Returns the `OData` property path for this property
    fn property_path(&self) -> &str;
}