Skip to content

Commit a7baf7f

Browse files
authored
refactor(docfx): split page-related functions (#10963)
I plan to add additional functions related to pages, and the one source file was getting too big already.
1 parent c7e5d7c commit a7baf7f

10 files changed

Lines changed: 249 additions & 179 deletions

docfx/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ cc_library(
3737
[cc_test(
3838
name = test.replace("/", "_").replace(".cc", ""),
3939
srcs = [test],
40+
copts = ["-std=c++17"],
4041
deps = [
4142
":docfx",
4243
"@com_github_zeux_pugixml//:pugixml",
@@ -47,6 +48,7 @@ cc_library(
4748
cc_binary(
4849
name = "doxygen2docfx",
4950
srcs = ["doxygen2docfx.cc"],
51+
copts = ["-std=c++17"],
5052
visibility = ["//visibility:private"],
5153
deps = [":docfx"],
5254
)

docfx/CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ include(EnableWerror)
2929

3030
add_library(
3131
docfx # cmake-format: sort
32-
doxygen2markdown.cc doxygen2markdown.h parse_arguments.cc parse_arguments.h)
32+
doxygen2markdown.cc doxygen2markdown.h doxygen_pages.cc doxygen_pages.h
33+
parse_arguments.cc parse_arguments.h)
3334
google_cloud_cpp_add_common_options(docfx)
3435
target_include_directories(docfx PUBLIC "${PROJECT_SOURCE_DIR}")
3536
target_compile_features(docfx PUBLIC cxx_std_17)
@@ -50,8 +51,9 @@ if (NOT BUILD_TESTING)
5051
return()
5152
endif ()
5253

53-
set(unit_tests # cmake-format: sort
54-
"doxygen2markdown_test.cc" "parse_arguments_test.cc")
54+
set(unit_tests
55+
# cmake-format: sort
56+
doxygen2markdown_test.cc doxygen_pages_test.cc parse_arguments_test.cc)
5557

5658
# Export the list of unit tests to a .bzl file so we do not need to maintain the
5759
# list in two places.

docfx/docfx.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818

1919
docfx_hdrs = [
2020
"doxygen2markdown.h",
21+
"doxygen_pages.h",
2122
"parse_arguments.h",
2223
]
2324

2425
docfx_srcs = [
2526
"doxygen2markdown.cc",
27+
"doxygen_pages.cc",
2628
"parse_arguments.cc",
2729
]

docfx/doxygen2markdown.cc

Lines changed: 0 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,9 @@
1313
// limitations under the License.
1414

1515
#include "docfx/doxygen2markdown.h"
16-
#include <iostream>
1716
#include <sstream>
18-
#include <string_view>
1917
#include <unordered_set>
2018

21-
namespace {
22-
2319
[[noreturn]] void UnknownChildType(std::string_view where,
2420
pugi::xml_node const& child) {
2521
std::ostringstream os;
@@ -36,96 +32,6 @@ namespace {
3632
throw std::runtime_error(std::move(os).str());
3733
}
3834

39-
} // namespace
40-
41-
// A "page" appears in the generated XML as:
42-
// clang-format off
43-
// <compounddef xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="indexpage" kind="page">
44-
// clang-format on
45-
//
46-
// That is, they are generic `compounddef` nodes -- the same entity used to
47-
// represent class or function reference docs. The definition is fairly complex
48-
// (see below). We will ignore things that we do not expect, such as
49-
// include diagrams, inner classes, etc.
50-
//
51-
// clang-format off
52-
// <xsd:complexType name="DoxygenType">
53-
// <xsd:sequence maxOccurs="unbounded">
54-
// <xsd:element name="compounddef" type="compounddefType" minOccurs="0" />
55-
// </xsd:sequence>
56-
// <xsd:attribute name="version" type="DoxVersionNumber" use="required" />
57-
// <xsd:attribute ref="xml:lang" use="required"/>
58-
// </xsd:complexType>
59-
//
60-
// <xsd:complexType name="compounddefType">
61-
// <xsd:sequence>
62-
// <xsd:element name="compoundname" type="xsd:string"/>
63-
// <xsd:element name="title" type="xsd:string" minOccurs="0" />
64-
// <xsd:element name="basecompoundref" type="compoundRefType" minOccurs="0" maxOccurs="unbounded" />
65-
// <xsd:element name="derivedcompoundref" type="compoundRefType" minOccurs="0" maxOccurs="unbounded" />
66-
// <xsd:element name="includes" type="incType" minOccurs="0" maxOccurs="unbounded" />
67-
// <xsd:element name="includedby" type="incType" minOccurs="0" maxOccurs="unbounded" />
68-
// <xsd:element name="incdepgraph" type="graphType" minOccurs="0" />
69-
// <xsd:element name="invincdepgraph" type="graphType" minOccurs="0" />
70-
// <xsd:element name="innerdir" type="refType" minOccurs="0" maxOccurs="unbounded" />
71-
// <xsd:element name="innerfile" type="refType" minOccurs="0" maxOccurs="unbounded" />
72-
// <xsd:element name="innerclass" type="refType" minOccurs="0" maxOccurs="unbounded" />
73-
// <xsd:element name="innernamespace" type="refType" minOccurs="0" maxOccurs="unbounded" />
74-
// <xsd:element name="innerpage" type="refType" minOccurs="0" maxOccurs="unbounded" />
75-
// <xsd:element name="innergroup" type="refType" minOccurs="0" maxOccurs="unbounded" />
76-
// <xsd:element name="templateparamlist" type="templateparamlistType" minOccurs="0" />
77-
// <xsd:element name="sectiondef" type="sectiondefType" minOccurs="0" maxOccurs="unbounded" />
78-
// <xsd:element name="tableofcontents" type="tableofcontentsType" minOccurs="0" maxOccurs="1" />
79-
// <xsd:element name="briefdescription" type="descriptionType" minOccurs="0" />
80-
// <xsd:element name="detaileddescription" type="descriptionType" minOccurs="0" />
81-
// <xsd:element name="inheritancegraph" type="graphType" minOccurs="0" />
82-
// <xsd:element name="collaborationgraph" type="graphType" minOccurs="0" />
83-
// <xsd:element name="programlisting" type="listingType" minOccurs="0" />
84-
// <xsd:element name="location" type="locationType" minOccurs="0" />
85-
// <xsd:element name="listofallmembers" type="listofallmembersType" minOccurs="0" />
86-
// </xsd:sequence>
87-
// <xsd:attribute name="id" type="xsd:string" />
88-
// <xsd:attribute name="kind" type="DoxCompoundKind" />
89-
// <xsd:attribute name="language" type="DoxLanguage" use="optional"/>
90-
// <xsd:attribute name="prot" type="DoxProtectionKind" />
91-
// <xsd:attribute name="final" type="DoxBool" use="optional"/>
92-
// <xsd:attribute name="inline" type="DoxBool" use="optional"/>
93-
// <xsd:attribute name="sealed" type="DoxBool" use="optional"/>
94-
// <xsd:attribute name="abstract" type="DoxBool" use="optional"/>
95-
// </xsd:complexType>
96-
// clang-format on
97-
std::string Page2Markdown(pugi::xml_node const& node) {
98-
if (std::string_view{node.name()} != "compounddef" ||
99-
std::string_view{node.attribute("kind").as_string()} != "page") {
100-
std::ostringstream os;
101-
os << "The node is not a page " << __func__ << "(): node=";
102-
node.print(os, /*indent=*/"", /*flags=*/pugi::format_raw,
103-
/*encoding=*/pugi::encoding_auto, /*depth=*/1);
104-
throw std::runtime_error(std::move(os).str());
105-
}
106-
std::stringstream os;
107-
MarkdownContext ctx;
108-
os << "# ";
109-
AppendTitle(os, ctx, node);
110-
os << "\n";
111-
for (auto const& child : node) {
112-
auto name = std::string_view(child.name());
113-
if (name == "compoundname") continue; // no markdown output
114-
if (name == "briefdescription") continue; // no markdown output
115-
if (name == "location") continue; // no markdown output
116-
if (name == "title") continue; // already handled
117-
// These are unexpected in a page: basecompoundref, derivedcompoundref,
118-
// includes, includedby, incdepgraph, invincdepgraph, innerdir,
119-
// innerfile, innerclass, innernamespace, innerpage, innergroup,
120-
// templateparamlist, sectiondef, inheritancegraph, collaborationgraph,
121-
// programlisting, listofallmembers.
122-
if (AppendIfDetailedDescription(os, ctx, child)) continue;
123-
UnknownChildType(__func__, child);
124-
}
125-
os << "\n";
126-
return std::move(os).str();
127-
}
128-
12935
// A "sect4" node type is defined as (note the lack of sect5):
13036
//
13137
// clang-format off

docfx/doxygen2markdown.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <pugixml.hpp>
1919
#include <iosfwd>
2020
#include <string>
21+
#include <string_view>
2122
#include <vector>
2223

2324
/**
@@ -33,12 +34,13 @@ struct MarkdownContext {
3334
std::vector<std::string> decorators;
3435
};
3536

36-
/**
37-
* Handle "page" nodes, such as the landing page of a library.
38-
*
39-
* This creates the root MarkdownContext, so no need to consume it.
40-
*/
41-
std::string Page2Markdown(pugi::xml_node const& node);
37+
/// Throws an exception indicating child node with an unknown type was found.
38+
[[noreturn]] void UnknownChildType(std::string_view where,
39+
pugi::xml_node const& child);
40+
41+
/// Throws an exception indicating an expected element is missing.
42+
[[noreturn]] void MissingElement(std::string_view where, std::string_view name,
43+
pugi::xml_node const& node);
4244

4345
/// Handles a sect4 node.
4446
bool AppendIfSect4(std::ostream& os, MarkdownContext const& ctx,

docfx/doxygen2markdown_test.cc

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -20,82 +20,6 @@ namespace {
2020
using ::testing::HasSubstr;
2121
using ::testing::Not;
2222

23-
TEST(Doxygen2Markdown, CommonPage) {
24-
auto constexpr kXml =
25-
R"xml(<?xml version="1.0" standalone="yes"?><doxygen version="1.9.1" xml:lang="en-US">
26-
<compounddef xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="indexpage" kind="page">
27-
<compoundname>index</compoundname>
28-
<title>Common Components for the Google Cloud C++ Client Libraries</title>
29-
<briefdescription>
30-
</briefdescription>
31-
<detaileddescription>
32-
<sect1 id="index_1autotoc_md0">
33-
<title>Overview</title>
34-
<para>This library contains common components shared by all the Google Cloud C++ Client Libraries. Including:</para>
35-
<para><itemizedlist>
36-
<listitem><para><ref refid="classgoogle_1_1cloud_1_1Credentials" kindref="compound">Credentials</ref> are used to configure authentication in the client libraries. See <ref refid="group__guac" kindref="compound">Authentication Components</ref> for more details on authentication.</para>
37-
</listitem><listitem><para><ref refid="classgoogle_1_1cloud_1_1Options" kindref="compound">Options</ref> are used to override the client library default configuration. See <ref refid="group__options" kindref="compound">Client Library Configuration</ref> for more details on library configuration.</para>
38-
</listitem><listitem><para><ref refid="classgoogle_1_1cloud_1_1Status" kindref="compound">Status</ref> error codes and details from an operation.</para>
39-
</listitem><listitem><para><ref refid="classgoogle_1_1cloud_1_1StatusOr" kindref="compound">StatusOr&lt;T&gt;</ref> returns a value on success and a <computeroutput>Status</computeroutput> on error.</para>
40-
</listitem><listitem><para><ref refid="classgoogle_1_1cloud_1_1future" kindref="compound">future&lt;T&gt;</ref> and <ref refid="classgoogle_1_1cloud_1_1promise" kindref="compound">promise&lt;T&gt;</ref> futures (a holder that will receive a value asynchronously) and promises (the counterpart of a future, where values are stored asynchronously). They satisfy the API for <computeroutput>std::future</computeroutput> and <computeroutput>std::promise</computeroutput>, and add support for callbacks and cancellation.</para>
41-
</listitem></itemizedlist>
42-
</para>
43-
<para>
44-
<simplesect kind="warning"><para>The symbols in the <computeroutput>google::cloud::internal</computeroutput> namespace are implementation details and subject to change and/or removal without notice.</para>
45-
</simplesect>
46-
<simplesect kind="warning"><para>The symbols in the <computeroutput>google::cloud::testing_util</computeroutput> namespace are implementation details and subject to change and/or removal without notice.</para>
47-
</simplesect>
48-
</para>
49-
<sect2 id="index_1autotoc_md1">
50-
<title>More information</title>
51-
<para><itemizedlist>
52-
<listitem><para><ref refid="common-error-handling" kindref="compound">Error Handling</ref> for more details about how the libraries report run-time errors and how you can handle them.</para>
53-
</listitem></itemizedlist>
54-
</para>
55-
</sect2>
56-
</sect1>
57-
</detaileddescription>
58-
<location file="doc/common-main.dox"/>
59-
</compounddef>
60-
</doxygen>)xml";
61-
62-
auto constexpr kExpected =
63-
R"md(# Common Components for the Google Cloud C++ Client Libraries
64-
65-
66-
## Overview
67-
68-
This library contains common components shared by all the Google Cloud C++ Client Libraries. Including:
69-
70-
71-
- [Credentials](xref:classgoogle_1_1cloud_1_1Credentials) are used to configure authentication in the client libraries. See [Authentication Components](xref:group__guac) for more details on authentication.
72-
- [Options](xref:classgoogle_1_1cloud_1_1Options) are used to override the client library default configuration. See [Client Library Configuration](xref:group__options) for more details on library configuration.
73-
- [Status](xref:classgoogle_1_1cloud_1_1Status) error codes and details from an operation.
74-
- [StatusOr<T>](xref:classgoogle_1_1cloud_1_1StatusOr) returns a value on success and a `Status` on error.
75-
- [future<T>](xref:classgoogle_1_1cloud_1_1future) and [promise<T>](xref:classgoogle_1_1cloud_1_1promise) futures (a holder that will receive a value asynchronously) and promises (the counterpart of a future, where values are stored asynchronously). They satisfy the API for `std::future` and `std::promise`, and add support for callbacks and cancellation.
76-
77-
78-
79-
> **Warning:**
80-
> The symbols in the `google::cloud::internal` namespace are implementation details and subject to change and/or removal without notice.
81-
82-
> **Warning:**
83-
> The symbols in the `google::cloud::testing_util` namespace are implementation details and subject to change and/or removal without notice.
84-
85-
### More information
86-
87-
88-
- [Error Handling](xref:common-error-handling) for more details about how the libraries report run-time errors and how you can handle them.
89-
)md";
90-
91-
pugi::xml_document doc;
92-
doc.load_string(kXml);
93-
auto selected = doc.select_node("//*[@id='indexpage']");
94-
ASSERT_TRUE(selected);
95-
auto const actual = Page2Markdown(selected.node());
96-
EXPECT_EQ(kExpected, actual);
97-
}
98-
9923
TEST(Doxygen2Markdown, Sect4) {
10024
auto constexpr kXml = R"xml(<?xml version="1.0" standalone="yes"?>
10125
<doxygen version="1.9.1" xml:lang="en-US">

docfx/doxygen_pages.cc

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "docfx/doxygen_pages.h"
16+
#include "docfx/doxygen2markdown.h"
17+
#include <iostream>
18+
#include <sstream>
19+
#include <string_view>
20+
21+
// A "page" appears in the generated XML as:
22+
// clang-format off
23+
// <compounddef xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="indexpage" kind="page">
24+
// clang-format on
25+
//
26+
// That is, they are generic `compounddef` nodes -- the same entity used to
27+
// represent class or function reference docs. The definition is fairly complex
28+
// (see below). We will ignore things that we do not expect, such as
29+
// include diagrams, inner classes, etc.
30+
//
31+
// clang-format off
32+
// <xsd:complexType name="DoxygenType">
33+
// <xsd:sequence maxOccurs="unbounded">
34+
// <xsd:element name="compounddef" type="compounddefType" minOccurs="0" />
35+
// </xsd:sequence>
36+
// <xsd:attribute name="version" type="DoxVersionNumber" use="required" />
37+
// <xsd:attribute ref="xml:lang" use="required"/>
38+
// </xsd:complexType>
39+
//
40+
// <xsd:complexType name="compounddefType">
41+
// <xsd:sequence>
42+
// <xsd:element name="compoundname" type="xsd:string"/>
43+
// <xsd:element name="title" type="xsd:string" minOccurs="0" />
44+
// <xsd:element name="basecompoundref" type="compoundRefType" minOccurs="0" maxOccurs="unbounded" />
45+
// <xsd:element name="derivedcompoundref" type="compoundRefType" minOccurs="0" maxOccurs="unbounded" />
46+
// <xsd:element name="includes" type="incType" minOccurs="0" maxOccurs="unbounded" />
47+
// <xsd:element name="includedby" type="incType" minOccurs="0" maxOccurs="unbounded" />
48+
// <xsd:element name="incdepgraph" type="graphType" minOccurs="0" />
49+
// <xsd:element name="invincdepgraph" type="graphType" minOccurs="0" />
50+
// <xsd:element name="innerdir" type="refType" minOccurs="0" maxOccurs="unbounded" />
51+
// <xsd:element name="innerfile" type="refType" minOccurs="0" maxOccurs="unbounded" />
52+
// <xsd:element name="innerclass" type="refType" minOccurs="0" maxOccurs="unbounded" />
53+
// <xsd:element name="innernamespace" type="refType" minOccurs="0" maxOccurs="unbounded" />
54+
// <xsd:element name="innerpage" type="refType" minOccurs="0" maxOccurs="unbounded" />
55+
// <xsd:element name="innergroup" type="refType" minOccurs="0" maxOccurs="unbounded" />
56+
// <xsd:element name="templateparamlist" type="templateparamlistType" minOccurs="0" />
57+
// <xsd:element name="sectiondef" type="sectiondefType" minOccurs="0" maxOccurs="unbounded" />
58+
// <xsd:element name="tableofcontents" type="tableofcontentsType" minOccurs="0" maxOccurs="1" />
59+
// <xsd:element name="briefdescription" type="descriptionType" minOccurs="0" />
60+
// <xsd:element name="detaileddescription" type="descriptionType" minOccurs="0" />
61+
// <xsd:element name="inheritancegraph" type="graphType" minOccurs="0" />
62+
// <xsd:element name="collaborationgraph" type="graphType" minOccurs="0" />
63+
// <xsd:element name="programlisting" type="listingType" minOccurs="0" />
64+
// <xsd:element name="location" type="locationType" minOccurs="0" />
65+
// <xsd:element name="listofallmembers" type="listofallmembersType" minOccurs="0" />
66+
// </xsd:sequence>
67+
// <xsd:attribute name="id" type="xsd:string" />
68+
// <xsd:attribute name="kind" type="DoxCompoundKind" />
69+
// <xsd:attribute name="language" type="DoxLanguage" use="optional"/>
70+
// <xsd:attribute name="prot" type="DoxProtectionKind" />
71+
// <xsd:attribute name="final" type="DoxBool" use="optional"/>
72+
// <xsd:attribute name="inline" type="DoxBool" use="optional"/>
73+
// <xsd:attribute name="sealed" type="DoxBool" use="optional"/>
74+
// <xsd:attribute name="abstract" type="DoxBool" use="optional"/>
75+
// </xsd:complexType>
76+
// clang-format on
77+
std::string Page2Markdown(pugi::xml_node const& node) {
78+
if (std::string_view{node.name()} != "compounddef" ||
79+
std::string_view{node.attribute("kind").as_string()} != "page") {
80+
std::ostringstream os;
81+
os << "The node is not a page " << __func__ << "(): node=";
82+
node.print(os, /*indent=*/"", /*flags=*/pugi::format_raw,
83+
/*encoding=*/pugi::encoding_auto, /*depth=*/1);
84+
throw std::runtime_error(std::move(os).str());
85+
}
86+
std::ostringstream os;
87+
MarkdownContext ctx;
88+
os << "# ";
89+
AppendTitle(os, ctx, node);
90+
os << "\n";
91+
for (auto const& child : node) {
92+
auto name = std::string_view(child.name());
93+
if (name == "compoundname") continue; // no markdown output
94+
if (name == "briefdescription") continue; // no markdown output
95+
if (name == "location") continue; // no markdown output
96+
if (name == "title") continue; // already handled
97+
// These are unexpected in a page: basecompoundref, derivedcompoundref,
98+
// includes, includedby, incdepgraph, invincdepgraph, innerdir,
99+
// innerfile, innerclass, innernamespace, innerpage, innergroup,
100+
// templateparamlist, sectiondef, inheritancegraph, collaborationgraph,
101+
// programlisting, listofallmembers.
102+
if (AppendIfDetailedDescription(os, ctx, child)) continue;
103+
UnknownChildType(__func__, child);
104+
}
105+
os << "\n";
106+
return std::move(os).str();
107+
}

0 commit comments

Comments
 (0)