From e7fc23b7417b968bd43d152c76c65d378a9ae3f4 Mon Sep 17 00:00:00 2001 From: Victor Benarbia Date: Sun, 16 Aug 2026 07:06:40 -0500 Subject: [PATCH 1/2] Add markdown() output support and Doxygen documentation Add Client::markdown() alongside html() for the new /md output format, bump the package version to 0.5.0, and document every serpapi.cpp method with Doxygen-style comments. Also adds a Doxyfile for generating HTML docs from src/ and the README. Co-Authored-By: Claude Sonnet 5 --- .gitignore | 3 ++ Doxyfile | 43 +++++++++++++++++++++++++ README.md | 9 ++++-- meson.build | 2 +- src/serpapi.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++- src/serpapi.hpp | 2 ++ test/test_serpapi.cpp | 20 ++++++++++++ 7 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 Doxyfile diff --git a/.gitignore b/.gitignore index 2dc52a5..0261326 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ # Build build +# Generated docs +docs + # Editor .vscode .cache diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..129dbba --- /dev/null +++ b/Doxyfile @@ -0,0 +1,43 @@ +# Doxyfile for serpapi-cpp +# +# Generate documentation with: +# doxygen Doxyfile +# Output is written to docs/html (open docs/html/index.html). +# +# This file only lists settings that differ from Doxygen's defaults; +# any option not listed here uses the standard Doxygen default value. + +#--------------------------------------------------------------------------- +# Project +#--------------------------------------------------------------------------- +PROJECT_NAME = "SerpApi C++" +PROJECT_BRIEF = "Official C++ client library for SerpApi" +PROJECT_NUMBER = 0.5.0 +OUTPUT_DIRECTORY = docs + +#--------------------------------------------------------------------------- +# Build +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +RECURSIVE = YES + +#--------------------------------------------------------------------------- +# Input +#--------------------------------------------------------------------------- +INPUT = src README.md +FILE_PATTERNS = *.cpp *.hpp +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Output formats +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +GENERATE_LATEX = NO + +#--------------------------------------------------------------------------- +# Preprocessing / diagrams +#--------------------------------------------------------------------------- +HAVE_DOT = NO +CLASS_DIAGRAMS = YES diff --git a/README.md b/README.md index ea6ebfc..ec47cc4 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ sudo apt update && sudo apt install -y rapidjson-dev Download and extract the latest release: ```bash -curl -sL https://github.com/serpapi/serpapi-cpp/archive/refs/tags/v0.4.1.tar.gz | tar xz -cd serpapi-0.4.1 +curl -sL https://github.com/serpapi/serpapi-cpp/archive/refs/tags/v0.5.0.tar.gz | tar xz +cd serpapi-0.5.0 meson setup build meson compile -C build sudo meson install -C build @@ -118,6 +118,9 @@ rapidjson::Document results = client.search(params); // raw search engine html as a String std::string raw_html = client.html(params); + +// raw search engine results as Markdown String +std::string raw_markdown = client.markdown(params); ``` [Google search documentation](https://serpapi.com/search-api). @@ -206,6 +209,7 @@ C++ versions validated by Github Actions: - C++20 ## Change logs + * [2026-08-16] 0.5.0 Add markdown() support via the output=md format. * [2026-06-26] 0.4.1 Exclude test dependencies from release build. * [2026-06-08] 0.4.0 Add unit tests and improve error handling. * [2026-02-12] 0.3.0 Refactor and cleanup. @@ -238,6 +242,7 @@ classDiagram parameter map search() Document html() String + markdown() String location() Document search_archive() Document account() Document diff --git a/meson.build b/meson.build index e39889f..aee8e86 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('serpapi', 'cpp', - version : '0.4.1', + version : '0.5.0', default_options : ['warning_level=3', 'cpp_std=c++17']) compiler = meson.get_compiler('cpp') diff --git a/src/serpapi.cpp b/src/serpapi.cpp index 27469a0..1e0169f 100644 --- a/src/serpapi.cpp +++ b/src/serpapi.cpp @@ -11,37 +11,83 @@ namespace serpapi { const static std::string HOST = "https://serpapi.com"; const static std::string NAME = "serpapi-cpp"; -const static std::string VERSION = "0.4.1"; +const static std::string VERSION = "0.5.0"; static std::once_flag curl_init_flag; +/** + * @brief Constructs a client with default parameters merged into every request. + * @param parameter Default parameters (e.g. api_key, engine) applied to all calls. + */ Client::Client(const std::map ¶meter) { this->parameter = parameter; } Client::~Client() {} +/** + * @brief Runs a search and returns the raw search engine result page as HTML. + * @param parameter Search parameters (e.g. q, location) merged with the client defaults. + * @return Raw HTML response body. + */ std::string Client::html(const std::map ¶meter) { GetResponse gr = Client::get("/search", "html", parameter); return gr.payload; } +/** + * @brief Runs a search and returns the raw search engine result page as Markdown. + * @param parameter Search parameters (e.g. q, location) merged with the client defaults. + * @return Raw Markdown response body. + */ +std::string Client::markdown(const std::map ¶meter) { + GetResponse gr = Client::get("/search", "md", parameter); + return gr.payload; +} + +/** + * @brief Runs a search and returns the parsed/structured results as JSON. + * @param parameter Search parameters (e.g. q, location) merged with the client defaults. + * @return Parsed JSON response as a rapidjson::Document. + */ rapidjson::Document Client::search(const std::map ¶meter) { return Client::json("/search", parameter); } +/** + * @brief Retrieves a previously run search from the search archive. + * @param id Search id, as returned in search_metadata.id from a prior search(). + * @return Parsed JSON response as a rapidjson::Document. + */ rapidjson::Document Client::search_archive(const std::string &id) { return Client::json("/searches/" + id + ".json", std::map()); } +/** + * @brief Retrieves account information (e.g. plan, usage) for the given api_key. + * @param parameter Request parameters (e.g. api_key) merged with the client defaults. + * @return Parsed JSON response as a rapidjson::Document. + */ rapidjson::Document Client::account(const std::map ¶meter) { return Client::json("/account.json", parameter); } +/** + * @brief Looks up supported locations matching a query. + * @param parameter Request parameters (e.g. q, limit) merged with the client defaults. + * @return Parsed JSON response as a rapidjson::Document. + */ rapidjson::Document Client::location(const std::map ¶meter) { return Client::json("/locations.json", parameter); } +/** + * @brief Fetches a JSON endpoint and parses it into a rapidjson::Document. + * @param uri Endpoint path, relative to HOST, to request with output=json. + * @param parameter Request parameters merged with the client defaults. + * @return Parsed JSON response, or a Document with an "error" member if the + * payload could not be parsed. + */ rapidjson::Document Client::json(const std::string &uri, const std::map ¶meter) { GetResponse gr = get(uri, "json", parameter); rapidjson::Document d; @@ -53,6 +99,12 @@ rapidjson::Document Client::json(const std::string &uri, const std::map ¶meter) { std::ostringstream oss; bool first = true; @@ -77,6 +129,17 @@ std::string encodeUrl(CURL *curl, const std::map ¶ return oss.str(); } +/** + * @brief Builds the full query string for a request. + * + * Concatenates call-specific parameters, then the client's default + * parameters, then the output format and source tag. + * + * @param curl Initialized CURL handle used to escape parameters. + * @param output Desired response format (e.g. "json", "html", "md"). + * @param parameter Call-specific parameters, merged with the client defaults. + * @return Fully encoded query string, without the leading '?'. + */ std::string Client::url(CURL *curl, const std::string &output, const std::map ¶meter) { std::string url_str = encodeUrl(curl, parameter); @@ -89,6 +152,15 @@ std::string Client::url(CURL *curl, const std::string &output, return url_str; } +/** + * @brief Performs the HTTP GET request against SerpApi. + * @param uri Endpoint path, relative to HOST (e.g. "/search"). + * @param output Desired response format (e.g. "json", "html", "md"). + * @param parameter Call-specific parameters, merged with the client defaults. + * @return HTTP status code and raw response body. httpCode is 0 on a + * client-side failure (CURL init or transport error), with the + * error message in payload. + */ GetResponse Client::get(const std::string &uri, const std::string &output, const std::map ¶meter) { std::call_once(curl_init_flag, []() { curl_global_init(CURL_GLOBAL_DEFAULT); }); diff --git a/src/serpapi.hpp b/src/serpapi.hpp index dcf1356..0e78614 100644 --- a/src/serpapi.hpp +++ b/src/serpapi.hpp @@ -32,6 +32,8 @@ class Client { std::string html(const std::map ¶meter = {}); + std::string markdown(const std::map ¶meter = {}); + rapidjson::Document search_archive(const std::string &search_id); rapidjson::Document diff --git a/test/test_serpapi.cpp b/test/test_serpapi.cpp index abd1519..340ab2e 100644 --- a/test/test_serpapi.cpp +++ b/test/test_serpapi.cpp @@ -116,3 +116,23 @@ TEST(client, html) { ASSERT_FALSE(html.empty()); ASSERT_TRUE(html.find("coffee") != std::string::npos); } + +TEST(client, markdown) { + const char *env_p = std::getenv("SERPAPI_KEY"); + if (env_p == nullptr) { + GTEST_SKIP() << "SERPAPI_KEY not set"; + } + std::string apiKey(env_p); + std::map default_parameter; + default_parameter["api_key"] = apiKey; + default_parameter["engine"] = "google"; + + serpapi::Client client(default_parameter); + + map parameter; + parameter["q"] = "coffee"; + + std::string markdown = client.markdown(parameter); + ASSERT_FALSE(markdown.empty()); + ASSERT_TRUE(markdown.find("coffee") != std::string::npos); +} From 42d3d00260dff4e1d48712f87bb7e600e3671a2c Mon Sep 17 00:00:00 2001 From: Victor Benarbia Date: Sun, 16 Aug 2026 11:07:35 -0500 Subject: [PATCH 2/2] Document latest code coverage results in README Records the current rake coverage output (lines, functions, branches) so contributors have a quick reference without re-running it locally. Co-Authored-By: Claude Sonnet 5 --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index ec47cc4..5644c8a 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,21 @@ C++ versions validated by Github Actions: - 100% tests passing: `rake test` - 100% code coverage: `rake coverage` +### Latest coverage report + +Generated with `rake coverage` (requires `SERPAPI_KEY` set to exercise the live API tests): + +| File | Lines | Exec | Cover | +|-------------------|------:|-----:|------:| +| src/callback.cpp | 11 | 8 | 72% | +| src/serpapi.cpp | 97 | 76 | 78% | +| src/serpapi.hpp | 1 | 1 | 100% | +| **TOTAL** | **109** | **85** | **78%** | + + - lines: 78.0% (85/109) + - functions: 100.0% (15/15) + - branches: 45.9% (79/172) + ## Design : UML diagram ### Class diagram ```mermaid