| // Copyright 2020 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 MEDIA_BASE_STATUS_H_ |
| #define MEDIA_BASE_STATUS_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/location.h" |
| #include "base/strings/string_piece.h" |
| #include "base/values.h" |
| #include "media/base/media_export.h" |
| #include "media/base/media_serializers_base.h" |
| #include "media/base/status_codes.h" |
| |
| // Mojo namespaces for serialization friend declarations. |
| namespace mojo { |
| template <typename T, typename U> |
| struct StructTraits; |
| } // namespace mojo |
| |
| namespace media { |
| |
| namespace mojom { |
| class StatusDataView; |
| } |
| |
| // Status is meant to be a relatively small (sizeof(void*) bytes) object |
| // that can be returned as a status value from functions or passed to callbacks |
| // that want a report of status. Status allows attaching of arbitrary named |
| // data, other Status' as causes, and stack frames, which can all be logged |
| // and reported throughout the media stack. The status code and message are |
| // immutable and can be used to give a stable numeric ID for any error |
| // generated by media code. |
| // There is also an OK state which can't hold any data and is only for |
| // successful returns. |
| class MEDIA_EXPORT Status { |
| public: |
| // Default constructor can be used for Status::Ok(); |
| Status(); |
| |
| // Constructor to create a new Status from a numeric code & message. |
| // These are immutable; if you'd like to change them, then you likely should |
| // create a new Status. {} or Status::Ok() should be used to create a |
| // success status. |
| // NOTE: This should never be given a location parameter when called - It is |
| // defaulted in order to grab the caller location. |
| Status(StatusCode code, |
| base::StringPiece message = "", |
| const base::Location& location = base::Location::Current()); |
| |
| // Copy Constructor & assignment. (Mojo uses both of these) |
| Status(const Status&); |
| Status& operator=(const Status&); |
| |
| // Allows move. |
| Status(Status&&); |
| Status& operator=(Status&&); |
| |
| // Needs an out of line destructor... |
| ~Status(); |
| |
| bool is_ok() const { return !data_; } |
| |
| // Getters for internal fields |
| const std::string& message() const { |
| DCHECK(data_); |
| return data_->message; |
| } |
| |
| StatusCode code() const { return data_ ? data_->code : StatusCode::kOk; } |
| |
| // Adds the current location to Status as it’s passed upwards. |
| // This does not need to be called at every location that touches it, but |
| // should be called for those locations where the path is ambiguous or |
| // critical. This can be especially helpful across IPC boundaries. This will |
| // fail on an OK status. |
| // NOTE: This should never be given a parameter when called - It is defaulted |
| // in order to grab the caller location. |
| Status&& AddHere( |
| const base::Location& location = base::Location::Current()) &&; |
| |
| // Add |cause| as the error that triggered this one. For example, |
| // DecoderStream might return kDecoderSelectionFailed with one or more causes |
| // that are the specific errors from the decoders that it tried. |
| Status&& AddCause(Status&& cause) &&; |
| void AddCause(Status&& cause) &; |
| |
| // Allows us to append any datatype which can be converted to |
| // an int/bool/string/base::Value. Any existing data associated with |key| |
| // will be overwritten by |value|. This will fail on an OK status. |
| template <typename T> |
| Status&& WithData(const char* key, const T& value) && { |
| DCHECK(data_); |
| data_->data.SetKey(key, MediaSerialize(value)); |
| return std::move(*this); |
| } |
| |
| template <typename T> |
| void WithData(const char* key, const T& value) & { |
| DCHECK(data_); |
| data_->data.SetKey(key, MediaSerialize(value)); |
| } |
| |
| private: |
| // Private helper to add the current stack frame to the error trace. |
| void AddFrame(const base::Location& location); |
| |
| // Keep the internal data in a unique ptr to minimize size of OK errors. |
| struct MEDIA_EXPORT StatusInternal { |
| StatusInternal(StatusCode code, std::string message); |
| ~StatusInternal(); |
| |
| // The current error code |
| StatusCode code = StatusCode::kOk; |
| |
| // The current error message (Can be used for |
| // https://developer.mozilla.org/en-US/docs/Web/API/Status) |
| std::string message; |
| |
| // Stack frames |
| std::vector<base::Value> frames; |
| |
| // Causes |
| std::vector<Status> causes; |
| |
| // Data attached to the error |
| base::Value data; |
| }; |
| |
| // Allow self-serialization |
| friend struct internal::MediaSerializer<Status>; |
| |
| // Allow mojo-serialization |
| friend struct std::allocator<Status>; |
| friend struct mojo::StructTraits<media::mojom::StatusDataView, Status>; |
| |
| // A null internals is an implicit OK. |
| std::unique_ptr<StatusInternal> data_; |
| }; |
| |
| // Convenience function to return |kOk|. |
| // OK won't have a message, trace, or data associated with them, and DCHECK |
| // if they are added. |
| MEDIA_EXPORT Status OkStatus(); |
| |
| // We need this two step macro to allow calling with no extra args - in a single |
| // step macro we would have no way of removing the trailing comma after the |
| // code. |
| #define STATUS(CODE_TRUNC, ...) \ |
| STATUS_INTERNAL(::media::StatusCode::CODE_TRUNC, ##__VA_ARGS__) |
| |
| #define STATUS_INTERNAL(...) ::media::Status(__VA_ARGS__) |
| |
| // Helper class to allow returning a |T| or a Status. Typical usage: |
| // |
| // ErrorOr<std::unique_ptr<MyObject>> FactoryFn() { |
| // if (success) |
| // return std::make_unique<MyObject>(); |
| // return Status(StatusCodes::kSomethingBadHappened); |
| // } |
| // |
| // auto result = FactoryFn(); |
| // if (result.has_error()) return std::move(result.error()); |
| // my_object_ = std::move(result.value()); |
| // |
| // Also useful if one would like to get an enum class return value, unless an |
| // error occurs: |
| // |
| // enum class ResultType { kNeedMoreInput, kOutputIsReady, kFormatChanged }; |
| // |
| // ErrorOr<ResultType> Foo() { ... } |
| // |
| // auto result = Foo(); |
| // if (result.has_error()) return std::move(result.error()); |
| // switch (result.value()) { |
| // case ResultType::kNeedMoreInput: |
| // ... |
| // } |
| template <typename T> |
| class ErrorOr { |
| public: |
| // All of these may be implicit, so that one may just return Status or |
| // the value in question. |
| ErrorOr(Status&& error) : error_(std::move(error)) {} |
| ErrorOr(const Status& error) : error_(error) {} |
| ErrorOr(T&& value) : value_(std::move(value)) {} |
| ErrorOr(const T& value) : value_(value) {} |
| |
| ~ErrorOr() = default; |
| |
| // Move- and copy- construction and assignment are okay. |
| ErrorOr(const ErrorOr&) = default; |
| ErrorOr(ErrorOr&&) = default; |
| ErrorOr& operator=(ErrorOr&) = default; |
| ErrorOr& operator=(ErrorOr&&) = default; |
| |
| // Do we have a value? |
| bool has_value() const { return value_.has_value(); } |
| |
| // Since we often test for errors, provide this too. |
| bool has_error() const { return !has_value(); } |
| |
| // Return the error, if we have one. Up to the caller to make sure that we |
| // have one via |!has_value()|. |
| Status& error() { return *error_; } |
| |
| // Return a ref to the value. It's up to the caller to verify that we have a |
| // value before calling this. |
| T& value() { return *value_; } |
| |
| private: |
| base::Optional<Status> error_; |
| base::Optional<T> value_; |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_BASE_STATUS_H_ |