/* * Copyright (c) 2022-2026, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace Media { template using DecoderErrorOr = ErrorOr; enum class DecoderErrorCategory : u8 { Aborted, Unknown, IO, NeedsMoreInput, EndOfStream, Memory, // The input is corrupted. Corrupted, // Invalid call. Invalid, // The input uses features that are not yet implemented. NotImplemented, }; class DecoderError { public: template T> static DecoderError with_description(DecoderErrorCategory category, T description) { return DecoderError(category, description); } template static DecoderError format(DecoderErrorCategory category, CheckedFormatString&& format_string, Parameters const&... parameters) { AK::VariadicFormatParams variadic_format_params { parameters... }; return DecoderError::with_description(category, ByteString::vformatted(format_string.view(), variadic_format_params)); } static DecoderError from_source_location(DecoderErrorCategory category, StringView description, SourceLocation location = SourceLocation::current()) { return DecoderError::format(category, "[{} @ {}:{}]: {}", location.function_name(), location.filename(), location.line_number(), description); } static DecoderError corrupted(StringView description, SourceLocation location = SourceLocation::current()) { return DecoderError::from_source_location(DecoderErrorCategory::Corrupted, description, location); } static DecoderError not_implemented(SourceLocation location = SourceLocation::current()) { return DecoderError::format(DecoderErrorCategory::NotImplemented, "{} is not implemented", location.function_name()); } DecoderErrorCategory category() const { return m_category; } StringView description() const LIFETIME_BOUND { return m_description.visit([](StringView x) { return x; }, [](ByteString const& x) { return x.view(); }); } // For compatibility with AK::Error StringView string_literal() const LIFETIME_BOUND { return description(); } private: template T> DecoderError(DecoderErrorCategory category, T description) : m_category(category) , m_description(move(description)) { } DecoderErrorCategory m_category { DecoderErrorCategory::Unknown }; Variant m_description; }; constexpr StringView decoder_error_category_to_string(DecoderErrorCategory category) { switch (category) { case DecoderErrorCategory::Aborted: return "Aborted"sv; case DecoderErrorCategory::Unknown: return "Unknown"sv; case DecoderErrorCategory::IO: return "IO"sv; case DecoderErrorCategory::NeedsMoreInput: return "NeedsMoreInput"sv; case DecoderErrorCategory::EndOfStream: return "EndOfStream"sv; case DecoderErrorCategory::Memory: return "Memory"sv; case DecoderErrorCategory::Corrupted: return "Corrupted"sv; case DecoderErrorCategory::Invalid: return "Invalid"sv; case DecoderErrorCategory::NotImplemented: return "NotImplemented"sv; } return "Invalid"sv; } #define DECODER_TRY(category, expression) \ ({ \ auto&& _result = ((expression)); \ if (_result.is_error()) [[unlikely]] { \ auto _error_string = _result.error().string_literal(); \ return DecoderError::from_source_location( \ ((category)), _error_string, SourceLocation::current()); \ } \ static_assert(!::AK::Detail::IsLvalueReference, \ "Do not return a reference from a fallible expression"); \ _result.release_value(); \ }) #define DECODER_TRY_ALLOC(expression) DECODER_TRY(DecoderErrorCategory::Memory, expression) } namespace AK { template<> struct Formatter : StandardFormatter { ErrorOr format(FormatBuilder& builder, Media::DecoderErrorCategory const& decoder_error_category) { return builder.put_literal(Media::decoder_error_category_to_string(decoder_error_category)); } }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Media::DecoderError const& decoder_error) { return Formatter::format(builder, "[DecoderError] ({}): {}"sv, decoder_error.category(), decoder_error.description()); } }; }