blob: b004b4e04a9057bfaf723a378bc06726c27555dc [file] [log] [blame]
/*
* Copyright (c) 2022 Project CHIP Authors
* All rights reserved.
*
* 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.
*/
#pragma once
#include <limits>
#include <stdint.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/DataModelTypes.h>
#include <lib/core/Optional.h>
#include <lib/support/TypeTraits.h>
#if CHIP_CONFIG_IM_STATUS_CODE_VERBOSE_FORMAT
#define ChipLogFormatIMStatus "0x%02x (%s)"
#define ChipLogValueIMStatus(status) chip::to_underlying(status), chip::Protocols::InteractionModel::StatusName(status)
#else // CHIP_CONFIG_IM_STATUS_CODE_VERBOSE_FORMAT
#define ChipLogFormatIMStatus "0x%02x"
#define ChipLogValueIMStatus(status) chip::to_underlying(status)
#endif // CHIP_CONFIG_IM_STATUS_CODE_VERBOSE_FORMAT
namespace chip {
namespace Protocols {
namespace InteractionModel {
enum class Status : uint8_t
{
#define CHIP_IM_STATUS_CODE(name, spec_name, value) name = value,
#include <protocols/interaction_model/StatusCodeList.h>
#undef CHIP_IM_STATUS_CODE
InvalidValue = ConstraintError, // Deprecated
};
#if CHIP_CONFIG_IM_STATUS_CODE_VERBOSE_FORMAT
const char * StatusName(Status status);
#endif // CHIP_CONFIG_IM_STATUS_CODE_VERBOSE_FORMAT
/**
* @brief Class to encapsulate a Status code, including possibly a
* cluster-specific code for generic SUCCESS/FAILURE.
*
* This abstractions joins together Status and ClusterStatus, which
* are the components of a StatusIB, used in many IM actions, in a
* way which allows both of them to carry together.
*
* This can be used everywhere a `Status` is used, but it is lossy
* to the cluster-specific code if used in place of `Status` when
* the cluster-specific code is set.
*
* This class can only be directly constructed from a `Status`. To
* attach a cluster-specific-code, please use the `ClusterSpecificFailure()`
* and `ClusterSpecificSuccess()` factory methods.
*/
class ClusterStatusCode
{
public:
explicit ClusterStatusCode(Status status) : mStatus(status) {}
// We only have simple copyable members, so we should be trivially copyable.
ClusterStatusCode(const ClusterStatusCode & other) = default;
ClusterStatusCode & operator=(const ClusterStatusCode & other) = default;
bool operator==(const ClusterStatusCode & other)
{
return (this->mStatus == other.mStatus) && (this->HasClusterSpecificCode() == other.HasClusterSpecificCode()) &&
(this->GetClusterSpecificCode() == other.GetClusterSpecificCode());
}
bool operator!=(const ClusterStatusCode & other) { return !(*this == other); }
ClusterStatusCode & operator=(const Status & status)
{
this->mStatus = status;
this->mClusterSpecificCode = chip::NullOptional;
return *this;
}
/**
* @brief Builder for a cluster-specific failure status code.
*
* @tparam T - enum type for the cluster-specific status code
* (e.g. chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum)
* @param cluster_specific_code - cluster-specific code to record with the failure
* (e.g. chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen)
* @return a ClusterStatusCode instance properly configured.
*/
template <typename T>
static ClusterStatusCode ClusterSpecificFailure(T cluster_specific_code)
{
static_assert(std::numeric_limits<std::underlying_type_t<T>>::max() <= std::numeric_limits<ClusterStatus>::max(),
"Type used must fit in uint8_t");
return ClusterStatusCode(Status::Failure, chip::to_underlying(cluster_specific_code));
}
/**
* @brief Builder for a cluster-specific success status code.
*
* @tparam T - enum type for the cluster-specific status code
* (e.g. chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum)
* @param cluster_specific_code - cluster-specific code to record with the success
* (e.g. chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kBasicWindowOpen)
* @return a ClusterStatusCode instance properly configured.
*/
template <typename T>
static ClusterStatusCode ClusterSpecificSuccess(T cluster_specific_code)
{
static_assert(std::numeric_limits<std::underlying_type_t<T>>::max() <= std::numeric_limits<ClusterStatus>::max(),
"Type used must fit in uint8_t");
return ClusterStatusCode(Status::Success, chip::to_underlying(cluster_specific_code));
}
/// @return true if the core Status associated with this ClusterStatusCode is the one for success.
bool IsSuccess() const { return mStatus == Status::Success; }
/// @return the core Status code associated withi this ClusterStatusCode.
Status GetStatus() const { return mStatus; }
/// @return true if a cluster-specific code is associated with the ClusterStatusCode.
bool HasClusterSpecificCode() const { return mClusterSpecificCode.HasValue(); }
/// @return the cluster-specific code associated with this ClusterStatusCode or chip::NullOptional if none is associated.
chip::Optional<ClusterStatus> GetClusterSpecificCode() const
{
if ((mStatus != Status::Failure) && (mStatus != Status::Success))
{
return chip::NullOptional;
}
return mClusterSpecificCode;
}
// Automatic conversions to common types, using the status code alone.
operator Status() const { return mStatus; }
private:
ClusterStatusCode() = delete;
ClusterStatusCode(Status status, ClusterStatus cluster_specific_code) :
mStatus(status), mClusterSpecificCode(chip::MakeOptional(cluster_specific_code))
{}
Status mStatus;
chip::Optional<ClusterStatus> mClusterSpecificCode;
};
static_assert(sizeof(ClusterStatusCode) <= sizeof(uint32_t), "ClusterStatusCode must not grow to be larger than a uint32_t");
} // namespace InteractionModel
} // namespace Protocols
} // namespace chip