From 97a6851525e79d39fe2b8f5a40672d3ba04ccb9d Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Tue, 29 Nov 2022 06:10:08 +0100
Subject: [PATCH] lib/curl/Error: std::system_error with CURLcode

---
 src/lib/curl/Adapter.cxx |  5 ++--
 src/lib/curl/Easy.hxx    |  5 ++--
 src/lib/curl/Error.hxx   | 57 ++++++++++++++++++++++++++++++++++++++++
 src/lib/curl/Init.cxx    |  3 ++-
 4 files changed, 64 insertions(+), 6 deletions(-)
 create mode 100644 src/lib/curl/Error.hxx

diff --git a/src/lib/curl/Adapter.cxx b/src/lib/curl/Adapter.cxx
index 6e1fadab3..aba94acc3 100644
--- a/src/lib/curl/Adapter.cxx
+++ b/src/lib/curl/Adapter.cxx
@@ -31,7 +31,6 @@
 #include "Easy.hxx"
 #include "Handler.hxx"
 #include "util/CharUtil.hxx"
-#include "util/RuntimeError.hxx"
 #include "util/StringSplit.hxx"
 #include "util/StringStrip.hxx"
 
@@ -98,8 +97,8 @@ CurlResponseHandlerAdapter::Done(CURLcode result) noexcept
 			StripRight(error_buffer);
 			const char *msg = error_buffer;
 			if (*msg == 0)
-				msg = curl_easy_strerror(result);
-			throw FormatRuntimeError("CURL failed: %s", msg);
+				msg = "CURL failed";
+			throw Curl::MakeError(result, msg);
 		}
 
 		FinishBody();
diff --git a/src/lib/curl/Easy.hxx b/src/lib/curl/Easy.hxx
index 8a434b719..fd2cceef0 100644
--- a/src/lib/curl/Easy.hxx
+++ b/src/lib/curl/Easy.hxx
@@ -30,6 +30,7 @@
 #ifndef CURL_EASY_HXX
 #define CURL_EASY_HXX
 
+#include "Error.hxx"
 #include "String.hxx"
 
 #include <curl/curl.h>
@@ -92,7 +93,7 @@ public:
 	void SetOption(CURLoption option, T value) {
 		CURLcode code = curl_easy_setopt(handle, option, value);
 		if (code != CURLE_OK)
-			throw std::runtime_error(curl_easy_strerror(code));
+			throw Curl::MakeError(code, "Failed to set option");
 	}
 
 	void SetPrivate(void *pointer) {
@@ -209,7 +210,7 @@ public:
 	void Perform() {
 		CURLcode code = curl_easy_perform(handle);
 		if (code != CURLE_OK)
-			throw std::runtime_error(curl_easy_strerror(code));
+			throw Curl::MakeError(code, "CURL failed");
 	}
 
 	bool Unpause() noexcept {
diff --git a/src/lib/curl/Error.hxx b/src/lib/curl/Error.hxx
new file mode 100644
index 000000000..f4c7131ac
--- /dev/null
+++ b/src/lib/curl/Error.hxx
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <curl/curl.h>
+
+#include <system_error>
+
+namespace Curl {
+
+class ErrorCategory final : public std::error_category {
+public:
+	const char *name() const noexcept override {
+		return "curl";
+	}
+
+	std::string message(int condition) const override {
+		return curl_easy_strerror(static_cast<CURLcode>(condition));
+	}
+};
+
+inline ErrorCategory error_category;
+
+inline std::system_error
+MakeError(CURLcode code, const char *msg) noexcept
+{
+	return std::system_error(static_cast<int>(code), error_category, msg);
+}
+
+} // namespace Curl
diff --git a/src/lib/curl/Init.cxx b/src/lib/curl/Init.cxx
index aa6e09efd..38f555467 100644
--- a/src/lib/curl/Init.cxx
+++ b/src/lib/curl/Init.cxx
@@ -29,6 +29,7 @@
 
 #include "Init.hxx"
 #include "Global.hxx"
+#include "Error.hxx"
 #include "event/Call.hxx"
 #include "thread/Mutex.hxx"
 
@@ -48,7 +49,7 @@ CurlInit::CurlInit(EventLoop &event_loop)
 
 	CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
 	if (code != CURLE_OK)
-		throw std::runtime_error(curl_easy_strerror(code));
+		throw Curl::MakeError(code, "CURL initialization failed");
 
 	assert(instance == nullptr);
 	instance = new CurlGlobal(event_loop);