summaryrefslogtreecommitdiff
path: root/chromium/media/base/status.h
blob: 8a7b4e0b9c75dc7f4036e7e0063af46c1517b954 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// 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:
  // This will create a kOk status, but please don't use it.  Use either
  // Status(StatusCode::kOk) or OkStatus().  This is here because the mojo
  // bindings assume that it is.
  // TODO(crbug.com/1106492): Remove this.
  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. Either {StatusCode::kOk} or OkStatus() may 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 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();

// TODO(liberato): Add more helper functions for common error returns.

// Helper class to allow returning a `T` or a Status.
//
// It is not okay to send a StatusOr with a status code of `kOk`.  `kOk` is
// reserved for cases where there is a `T` rather than a Status.
//
// Typical usage:
//
// StatusOr<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();
//
// Can also be combined into a single switch using `code()`:
//
// switch (result.code()) {
//  case StatusCode::kOk:
//    // `kOk` is special; it means the StatusOr has a `T`.
//    // Do something with result.value()
//    break;
//  // Maybe switch on specific non-kOk codes for special processing.
//  default:  // Send unknown errors upwards.
//    return std::move(result).error();
// }
//
// Also useful if one would like to get an enum class return value, unless an
// error occurs:
//
// enum class ResultType { kNeedMoreInput, kOutputIsReady, kFormatChanged };
//
// StatusOr<ResultType> Foo() { ... }
//
// auto result = Foo();
// if (result.has_error()) return std::move(result).error();
// switch (std::move(result).value()) {
//  case ResultType::kNeedMoreInput:
//   ...
// }
template <typename T>
class StatusOr {
 public:
  // All of these may be implicit, so that one may just return Status or
  // the value in question.
  /* not explicit */ StatusOr(Status&& error) : error_(std::move(error)) {
    DCHECK_NE(code(), StatusCode::kOk);
  }
  /* not explicit */ StatusOr(const Status& error) : error_(error) {
    DCHECK_NE(code(), StatusCode::kOk);
  }
  StatusOr(StatusCode code,
           const base::Location& location = base::Location::Current())
      : error_(Status(code, "", location)) {
    DCHECK_NE(code, StatusCode::kOk);
  }

  StatusOr(T&& value) : value_(std::move(value)) {}
  StatusOr(const T& value) : value_(value) {}

  ~StatusOr() = default;

  // Move- and copy- construction and assignment are okay.
  StatusOr(const StatusOr&) = default;
  StatusOr(StatusOr&&) = default;
  StatusOr& operator=(StatusOr&) = default;
  StatusOr& operator=(StatusOr&&) = default;

  // Do we have a value?
  bool has_value() const { return value_.has_value(); }

  // Do we have an error?
  bool has_error() const { return error_.has_value(); }

  // Return the error, if we have one.  Up to the caller to make sure that we
  // have one via |has_error()|.
  // NOTE: once this is called, the StatusOr is defunct and should not be used.
  Status error() && {
    CHECK(error_);
    auto error = std::move(*error_);
    error_.reset();
    return error;
  }

  // Return the value.  It's up to the caller to verify that we have a value
  // before calling this.  Also, this only works once, after which we will have
  // an error.  Use like this: std::move(status_or).value();
  // NOTE: once this is called, the StatusOr is defunct and should not be used.
  T value() && {
    CHECK(value_);
    auto value = std::move(std::get<0>(*value_));
    value_.reset();
    return value;
  }

  // Returns the error code we have, if any, or `kOk`.
  StatusCode code() const {
    CHECK(error_ || value_);
    return error_ ? error_->code() : StatusCode::kOk;
  }

 private:
  // Optional error.
  base::Optional<Status> error_;
  // We wrap |T| in a container so that windows COM wrappers work.  They
  // override operator& and similar, and won't compile in a base::Optional.
  base::Optional<std::tuple<T>> value_;
};

}  // namespace media

#endif  // MEDIA_BASE_STATUS_H_