blob: ad18a91d1fdbfc34196d3800515ccfb8ba68bf05 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PLATFORM_BASE_ERROR_H_
#define PLATFORM_BASE_ERROR_H_
#include <cassert>
#include <ostream>
#include <string>
#include <utility>
#include "platform/base/macros.h"
namespace openscreen {
// Represents an error returned by an OSP library operation. An error has a
// code and an optional message.
class Error {
public:
// TODO(crbug.com/openscreen/65): Group/rename OSP-specific errors
enum class Code : int8_t {
// No error occurred.
kNone = 0,
// A transient condition prevented the operation from proceeding (e.g.,
// cannot send on a non-blocking socket without blocking). This indicates
// the caller should try again later.
kAgain = -1,
// CBOR errors.
kCborParsing = 1,
kCborEncoding,
kCborIncompleteMessage,
kCborInvalidResponseId,
kCborInvalidMessage,
// Presentation start errors.
kNoAvailableReceivers,
kRequestCancelled,
kNoPresentationFound,
kPreviousStartInProgress,
kUnknownStartError,
kUnknownRequestId,
kAddressInUse,
kDomainNameTooLong,
kDomainNameLabelTooLong,
kIOFailure,
kInitializationFailure,
kInvalidIPV4Address,
kInvalidIPV6Address,
kConnectionFailed,
kSocketOptionSettingFailure,
kSocketAcceptFailure,
kSocketBindFailure,
kSocketClosedFailure,
kSocketConnectFailure,
kSocketInvalidState,
kSocketListenFailure,
kSocketReadFailure,
kSocketSendFailure,
// MDNS errors.
kMdnsRegisterFailure,
kMdnsReadFailure,
kMdnsNonConformingFailure,
kParseError,
kUnknownMessageType,
kNoActiveConnection,
kAlreadyClosed,
kInvalidConnectionState,
kNoStartedPresentation,
kPresentationAlreadyStarted,
kJsonParseError,
kJsonWriteError,
// OpenSSL errors.
// Was unable to generate an RSA key.
kRSAKeyGenerationFailure,
kRSAKeyParseError,
// Was unable to initialize an EVP_PKEY type.
kEVPInitializationError,
// Was unable to generate a certificate.
kCertificateCreationError,
// Certificate failed validation.
kCertificateValidationError,
// Failed to produce a hashing digest.
kSha256HashFailure,
// A non-recoverable SSL library error has occurred.
kFatalSSLError,
kFileLoadFailure,
// Cast certificate errors.
// Certificates were not provided for verification.
kErrCertsMissing,
// The certificates provided could not be parsed.
kErrCertsParse,
// Key usage is missing or is not set to Digital Signature.
// This error could also be thrown if the CN is missing.
kErrCertsRestrictions,
// The current date is before the notBefore date or after the notAfter date.
kErrCertsDateInvalid,
// The certificate failed to chain to a trusted root.
kErrCertsVerifyGeneric,
// The certificate was not found in the trust store.
kErrCertsVerifyUntrustedCert,
// The CRL is missing or failed to verify.
kErrCrlInvalid,
// One of the certificates in the chain is revoked.
kErrCertsRevoked,
// The pathlen constraint of the root certificate was exceeded.
kErrCertsPathlen,
// Cast authentication errors.
kCastV2PeerCertEmpty,
kCastV2WrongPayloadType,
kCastV2NoPayload,
kCastV2PayloadParsingFailed,
kCastV2MessageError,
kCastV2NoResponse,
kCastV2FingerprintNotFound,
kCastV2CertNotSignedByTrustedCa,
kCastV2CannotExtractPublicKey,
kCastV2SignedBlobsMismatch,
kCastV2TlsCertValidityPeriodTooLong,
kCastV2TlsCertValidStartDateInFuture,
kCastV2TlsCertExpired,
kCastV2SenderNonceMismatch,
kCastV2DigestUnsupported,
kCastV2SignatureEmpty,
// Cast channel errors.
kCastV2ChannelNotOpen,
kCastV2AuthenticationError,
kCastV2ConnectError,
kCastV2CastSocketError,
kCastV2TransportError,
kCastV2InvalidMessage,
kCastV2InvalidChannelId,
kCastV2ConnectTimeout,
kCastV2PingTimeout,
kCastV2ChannelPolicyMismatch,
kCreateSignatureFailed,
// Discovery errors.
kUpdateReceivedRecordFailure,
kRecordPublicationError,
kProcessReceivedRecordFailure,
// Generic errors.
kUnknownError,
kNotImplemented,
kInsufficientBuffer,
kParameterInvalid,
kParameterOutOfRange,
kParameterNullPointer,
kIndexOutOfBounds,
kItemAlreadyExists,
kItemNotFound,
kOperationInvalid,
kOperationInProgress,
kOperationCancelled,
// Cast streaming errors
kUnknownCodec,
};
Error();
Error(const Error& error);
Error(Error&& error) noexcept;
Error(Code code); // NOLINT
Error(Code code, const std::string& message);
Error(Code code, std::string&& message);
~Error();
Error& operator=(const Error& other);
Error& operator=(Error&& other);
bool operator==(const Error& other) const;
bool operator!=(const Error& other) const;
// Special case comparison with codes. Without this case, comparisons will
// not work as expected, e.g.
// const Error foo(Error::Code::kItemNotFound, "Didn't find an item");
// foo == Error::Code::kItemNotFound is actually false.
bool operator==(Code code) const;
bool operator!=(Code code) const;
bool ok() const { return code_ == Code::kNone; }
Code code() const { return code_; }
const std::string& message() const { return message_; }
std::string& message() { return message_; }
static const Error& None();
std::string ToString() const;
private:
Code code_ = Code::kNone;
std::string message_;
};
std::ostream& operator<<(std::ostream& os, const Error::Code& code);
std::ostream& operator<<(std::ostream& out, const Error& error);
// A convenience function to return a single value from a function that can
// return a value or an error. For normal results, construct with a ValueType*
// (ErrorOr takes ownership) and the Error will be kNone with an empty message.
// For Error results, construct with an error code and value.
//
// Example:
//
// ErrorOr<Bar> Foo::DoSomething() {
// if (success) {
// return Bar();
// } else {
// return Error(kBadThingHappened, "No can do");
// }
// }
//
// TODO(mfoltz): Add support for type conversions.
template <typename ValueType>
class ErrorOr {
public:
static ErrorOr<ValueType> None() {
static ErrorOr<ValueType> error(Error::Code::kNone);
return error;
}
ErrorOr(const ValueType& value) : value_(value), is_value_(true) {} // NOLINT
ErrorOr(ValueType&& value) noexcept // NOLINT
: value_(std::move(value)), is_value_(true) {}
ErrorOr(const Error& error) : error_(error), is_value_(false) { // NOLINT
assert(error_.code() != Error::Code::kNone);
}
ErrorOr(Error&& error) noexcept // NOLINT
: error_(std::move(error)), is_value_(false) {
assert(error_.code() != Error::Code::kNone);
}
ErrorOr(Error::Code code) : error_(code), is_value_(false) { // NOLINT
assert(error_.code() != Error::Code::kNone);
}
ErrorOr(Error::Code code, std::string message)
: error_(code, std::move(message)), is_value_(false) {
assert(error_.code() != Error::Code::kNone);
}
ErrorOr(const ErrorOr& other) = delete;
ErrorOr(ErrorOr&& other) noexcept : is_value_(other.is_value_) {
// NB: Both |value_| and |error_| are uninitialized memory at this point!
// Unlike the other constructors, the compiler will not auto-generate
// constructor calls for either union member because neither appeared in
// this constructor's initializer list.
if (other.is_value_) {
new (&value_) ValueType(std::move(other.value_));
} else {
new (&error_) Error(std::move(other.error_));
}
}
ErrorOr& operator=(const ErrorOr& other) = delete;
ErrorOr& operator=(ErrorOr&& other) noexcept {
this->~ErrorOr<ValueType>();
new (this) ErrorOr<ValueType>(std::move(other));
return *this;
}
~ErrorOr() {
// NB: |value_| or |error_| must be explicitly destroyed since the compiler
// will not auto-generate the destructor calls for union members.
if (is_value_) {
value_.~ValueType();
} else {
error_.~Error();
}
}
bool is_error() const { return !is_value_; }
bool is_value() const { return is_value_; }
// Unlike Error, we CAN provide an operator bool here, since it is
// more obvious to callers that ErrorOr<Foo> will be true if it's Foo.
operator bool() const { return is_value_; }
const Error& error() const {
assert(!is_value_);
return error_;
}
Error& error() {
assert(!is_value_);
return error_;
}
const ValueType& value() const {
assert(is_value_);
return value_;
}
ValueType& value() {
assert(is_value_);
return value_;
}
// Move only value or fallback
ValueType&& value(ValueType&& fallback) {
if (is_value()) {
return std::move(value());
}
return std::forward<ValueType>(fallback);
}
// Copy only value or fallback
ValueType value(ValueType fallback) const {
if (is_value()) {
return value();
}
return std::move(fallback);
}
private:
// Only one of these is an active member, determined by |is_value_|. Since
// they are union'ed, they must be explicitly constructed and destroyed.
union {
ValueType value_;
Error error_;
};
// If true, |value_| is initialized and active. Otherwise, |error_| is
// initialized and active.
const bool is_value_;
};
// Define comparison operators using SFINAE.
template <typename ValueType>
bool operator<(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
// Handle the cases where one side is an error.
if (lhs.is_error() != rhs.is_error()) {
return lhs.is_error();
}
// Handle the case where both sides are errors.
if (lhs.is_error()) {
return static_cast<int8_t>(lhs.error().code()) <
static_cast<int8_t>(rhs.error().code());
}
// Handle the case where both are values.
return lhs.value() < rhs.value();
}
template <typename ValueType>
bool operator>(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
return rhs < lhs;
}
template <typename ValueType>
bool operator<=(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
return !(lhs > rhs);
}
template <typename ValueType>
bool operator>=(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
return !(rhs < lhs);
}
template <typename ValueType>
bool operator==(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
// Handle the cases where one side is an error.
if (lhs.is_error() != rhs.is_error()) {
return false;
}
// Handle the case where both sides are errors.
if (lhs.is_error()) {
return lhs.error() == rhs.error();
}
// Handle the case where both are values.
return lhs.value() == rhs.value();
}
template <typename ValueType>
bool operator!=(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
return !(lhs == rhs);
}
} // namespace openscreen
#endif // PLATFORM_BASE_ERROR_H_