| // Copyright 2026 The Abseil Authors | |
| // | |
| // 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 | |
| // | |
| // https://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. | |
| #include "absl/status/status_builder.h" | |
| #include <cstdint> | |
| #include <memory> | |
| #include <ostream> | |
| #include <string> | |
| #include <type_traits> | |
| #include <utility> | |
| #include "absl/base/attributes.h" | |
| #include "absl/base/config.h" | |
| #include "absl/base/macros.h" | |
| #include "absl/status/status.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/time/time.h" | |
| #include "absl/types/source_location.h" | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| void StatusBuilder::Destroy(std::unique_ptr<Rep>) { | |
| // nothing to do. The unique_ptr will do the cleanup. | |
| } | |
| // These constructors are not-inlined and defined in the .cc file to reduce | |
| // binary size. See cl/354351433 for a quantification. | |
| StatusBuilder::StatusBuilder() = default; | |
| StatusBuilder::StatusBuilder(const absl::Status& original_status, | |
| absl::SourceLocation location) | |
| : loc_(location), rep_(InitRep(original_status)) {} | |
| StatusBuilder::operator absl::Status() const& { | |
| if (rep_ == nullptr) return absl::Status(); | |
| return CreateStatusAndConditionallyLog(loc_, std::make_unique<Rep>(*rep_)); | |
| } | |
| StatusBuilder::Rep::Rep(const absl::Status& s) : status(s) {} | |
| StatusBuilder::Rep::Rep(absl::Status&& s) : status(std::move(s)) {} | |
| StatusBuilder::Rep::~Rep() = default; | |
| StatusBuilder::Rep* StatusBuilder::InitRepImpl(absl::Status s) { | |
| if (s.ok()) { | |
| return nullptr; | |
| } else { | |
| return new Rep(std::move(s)); | |
| } | |
| } | |
| StatusBuilder::Rep::Rep(const Rep& r) | |
| : status(r.status), | |
| logging_mode(r.logging_mode), | |
| log_severity(r.log_severity), | |
| verbose_level(r.verbose_level), | |
| n(r.n), | |
| period(r.period), | |
| stream_message(r.stream_message), | |
| sink(r.sink), | |
| message_join_style(r.message_join_style), | |
| should_log_stack_trace(r.should_log_stack_trace), | |
| also_send_to_log(r.also_send_to_log) { | |
| if (r.stream.has_value()) { | |
| InitStream(); | |
| } | |
| } | |
| void StatusBuilder::Rep::InitStream() { stream.emplace(stream_message); } | |
| bool StatusBuilder::HasPayload() const { | |
| static constexpr absl::string_view kMessageSetUrl = | |
| "type.googleapis.com/util.MessageSetPayload"; | |
| return rep_ != nullptr && rep_->status.GetPayload(kMessageSetUrl).has_value(); | |
| } | |
| ABSL_ATTRIBUTE_WEAK StatusBuilder& StatusBuilder::SetCode( | |
| absl::StatusCode code) & { | |
| if (rep_ == nullptr) { | |
| rep_ = std::make_unique<StatusBuilder::Rep>( | |
| absl::Status(code, absl::string_view(), absl::SourceLocation())); | |
| } else { | |
| absl::Status status(code, absl::string_view(), absl::SourceLocation()); | |
| rep_->status.ForEachPayload( | |
| [&status](absl::string_view type_url, const absl::Cord& payload) { | |
| status.SetPayload(type_url, payload); | |
| }); | |
| rep_->status = std::move(status); | |
| } | |
| return *this; | |
| } | |
| ABSL_ATTRIBUTE_WEAK void AbslInternalSetErrorCode(StatusBuilder& builder, | |
| absl::StatusCode code) { | |
| builder.SetCode(code); | |
| } | |
| class status_internal::StatusPrivateAccessorForStatusBuilder { | |
| public: | |
| static absl::Status SetMessage(const absl::Status& status, | |
| absl::string_view message) { | |
| ABSL_ASSERT(!status.ok()); | |
| if (message.empty()) { | |
| return absl::Status(status.code(), message, absl::SourceLocation()); | |
| } | |
| using StatusRep = | |
| std::remove_cv_t<std::remove_pointer_t<decltype(Status::RepToPointer( | |
| std::declval<uintptr_t>()))>>; | |
| StatusRep* rep; | |
| if (Status::IsInlined(status.rep_)) { | |
| rep = new StatusRep(Status::InlinedRepToCode(status.rep_), message, | |
| nullptr); | |
| } else { | |
| rep = Status::RepToPointer(status.rep_)->Clone(message, true, true); | |
| } | |
| return absl::Status(Status::PointerToRep(rep)); | |
| } | |
| static absl::Status JoinMessageToStatus(absl::Status s, absl::string_view msg, | |
| MessageJoinStyle style) { | |
| if (s.ok() || msg.empty()) return s; | |
| const absl::string_view original_message = s.message(); | |
| switch (style) { | |
| case MessageJoinStyle::kAnnotate: { | |
| std::string annotated; | |
| if (!original_message.empty()) { | |
| absl::StrAppend(&annotated, original_message, "; ", msg); | |
| msg = annotated; | |
| } | |
| return SetMessage(s, msg); | |
| } | |
| case MessageJoinStyle::kPrepend: | |
| return SetMessage(s, absl::StrCat(msg, original_message)); | |
| case MessageJoinStyle::kAppend: | |
| return SetMessage(s, absl::StrCat(original_message, msg)); | |
| default: | |
| return absl::InternalError("Unknown MessageJoinStyle"); | |
| } | |
| } | |
| }; | |
| ABSL_ATTRIBUTE_WEAK std::string StatusBuilder::CurrentStackTrace() { | |
| return std::string(); | |
| } | |
| ABSL_ATTRIBUTE_WEAK absl::Status StatusBuilder::CreateStatusAndConditionallyLog( | |
| absl::SourceLocation loc, std::unique_ptr<Rep> rep) { | |
| if (rep == nullptr) return absl::OkStatus(); | |
| absl::Status result = status_internal::StatusPrivateAccessorForStatusBuilder:: | |
| JoinMessageToStatus(std::move(rep->status), rep->stream_message, | |
| rep->message_join_style); | |
| // Passing in the `loc` last to ensure the sequence of the source locations. | |
| result.AddSourceLocation(loc); | |
| return result; | |
| } | |
| ABSL_ATTRIBUTE_WEAK std::string StatusBuilder::ToString() const { | |
| if (rep_ == nullptr) { | |
| return absl::OkStatus().ToString(); | |
| } | |
| return status_internal::StatusPrivateAccessorForStatusBuilder:: | |
| JoinMessageToStatus(rep_->status, rep_->stream_message, | |
| rep_->message_join_style) | |
| .WithSourceLocation(loc_) | |
| .ToString(); | |
| } | |
| ABSL_ATTRIBUTE_WEAK std::ostream& operator<<(std::ostream& os, | |
| const StatusBuilder& builder) { | |
| return os << static_cast<absl::Status>(builder); | |
| } | |
| ABSL_ATTRIBUTE_WEAK std::ostream& operator<<(std::ostream& os, | |
| StatusBuilder&& builder) { | |
| return os << static_cast<absl::Status>(std::move(builder)); | |
| } | |
| ABSL_NAMESPACE_END | |
| } // namespace absl |