Skip to content

Commit 7ecb7b3

Browse files
authored
impl(docfx): scaffolding for conversion functions (#10763)
This PR introduces the first function to convert simple paragraphs in Doxygen XML to MarkDown strings.
1 parent 1e003e4 commit 7ecb7b3

5 files changed

Lines changed: 192 additions & 4 deletions

File tree

docfx/CMakeLists.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ if (NOT GOOGLE_CLOUD_CPP_ENABLE_CXX_EXCEPTIONS)
2323
return()
2424
endif ()
2525

26+
find_package(pugixml CONFIG REQUIRED)
27+
2628
include(EnableWerror)
2729

28-
add_library(docfx # cmake-format: sort
29-
parse_arguments.cc parse_arguments.h)
30+
add_library(
31+
docfx # cmake-format: sort
32+
doxygen2markdown.cc doxygen2markdown.h parse_arguments.cc parse_arguments.h)
3033
google_cloud_cpp_add_common_options(docfx)
3134
target_include_directories(docfx PUBLIC "${PROJECT_SOURCE_DIR}")
3235
target_compile_features(docfx PUBLIC cxx_std_17)
36+
target_link_libraries(docfx PUBLIC pugixml::pugixml)
3337

3438
add_executable(doxygen2docfx doxygen2docfx.cc)
3539
target_link_libraries(doxygen2docfx PUBLIC docfx)
@@ -42,7 +46,7 @@ if (NOT BUILD_TESTING)
4246
endif ()
4347

4448
set(unit_tests # cmake-format: sort
45-
"parse_arguments_test.cc")
49+
"doxygen2markdown_test.cc" "parse_arguments_test.cc")
4650

4751
foreach (fname IN LISTS unit_tests)
4852
google_cloud_cpp_add_executable(target "docfx" "${fname}")

docfx/doxygen2markdown.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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/doxygen2markdown.h"
16+
#include <iostream>
17+
#include <sstream>
18+
#include <string_view>
19+
20+
namespace {
21+
22+
[[noreturn]] void UnknownChildType(std::string_view where,
23+
pugi::xml_node const& child) {
24+
std::ostringstream os;
25+
os << "Unknown child in " << where << "(): node=";
26+
child.print(os, /*indent=*/"", /*flags=*/pugi::format_raw);
27+
throw std::runtime_error(std::move(os).str());
28+
}
29+
30+
} // namespace
31+
32+
bool AppendIfPlainText(std::ostream& os, pugi::xml_node const& node) {
33+
if (!std::string_view{node.name()}.empty() || !node.attributes().empty()) {
34+
return false;
35+
}
36+
os << node.value();
37+
return true;
38+
}
39+
40+
bool AppendIfComputerOutput(std::ostream& os, pugi::xml_node const& node) {
41+
if (std::string_view{node.name()} != "computeroutput") return false;
42+
os << '`' << node.child_value() << '`';
43+
return true;
44+
}
45+
46+
bool AppendIfParagraph(std::ostream& os, pugi::xml_node const& node) {
47+
if (std::string_view{node.name()} != "para") return false;
48+
for (auto const& child : node) {
49+
if (AppendIfPlainText(os, child)) continue;
50+
if (AppendIfComputerOutput(os, child)) continue;
51+
UnknownChildType(__func__, child);
52+
}
53+
os << "\n";
54+
return true;
55+
}

docfx/doxygen2markdown.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
#ifndef GOOGLE_CLOUD_CPP_DOCFX_DOXYGEN2MARKDOWN_H
16+
#define GOOGLE_CLOUD_CPP_DOCFX_DOXYGEN2MARKDOWN_H
17+
18+
#include <iosfwd>
19+
#include <pugixml.hpp>
20+
21+
bool AppendIfPlainText(std::ostream& os, pugi::xml_node const& node);
22+
bool AppendIfComputerOutput(std::ostream& os, pugi::xml_node const& node);
23+
bool AppendIfParagraph(std::ostream& os, pugi::xml_node const& node);
24+
25+
#endif // GOOGLE_CLOUD_CPP_DOCFX_DOXYGEN2MARKDOWN_H

docfx/doxygen2markdown_test.cc

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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/doxygen2markdown.h"
16+
#include <gmock/gmock.h>
17+
#include <sstream>
18+
19+
namespace {
20+
21+
using ::testing::HasSubstr;
22+
using ::testing::IsEmpty;
23+
using ::testing::Not;
24+
25+
TEST(Doxygen2Markdown, PlainText) {
26+
pugi::xml_document doc;
27+
doc.load_string(R"xml(<?xml version="1.0" standalone="yes"?>
28+
<doxygen version="1.9.1" xml:lang="en-US">
29+
<para id="plain-text">test-only-value 42</para>
30+
</doxygen>)xml");
31+
auto selected = doc.select_node("//*[@id='plain-text']");
32+
ASSERT_TRUE(selected);
33+
std::ostringstream os;
34+
ASSERT_TRUE(AppendIfPlainText(os, *selected.node().children().begin()));
35+
EXPECT_EQ(os.str(), "test-only-value 42");
36+
}
37+
38+
TEST(Doxygen2Markdown, ComputerOutput) {
39+
pugi::xml_document doc;
40+
doc.load_string(R"xml(<?xml version="1.0" standalone="yes"?>
41+
<doxygen version="1.9.1" xml:lang="en-US">
42+
<computeroutput id="test-node">int f() { return 42; }</computeroutput>
43+
</doxygen>)xml");
44+
auto selected = doc.select_node("//*[@id='test-node']");
45+
std::ostringstream os;
46+
ASSERT_TRUE(AppendIfComputerOutput(os, selected.node()));
47+
EXPECT_EQ(os.str(), "`int f() { return 42; }`");
48+
}
49+
50+
TEST(Doxygen2Markdown, Paragraph) {
51+
pugi::xml_document doc;
52+
doc.load_string(R"xml(<?xml version="1.0" standalone="yes"?>
53+
<doxygen version="1.9.1" xml:lang="en-US">
54+
<para id='test-node'>Try using <computeroutput id="test-node">int f() { return 42; }</computeroutput> in your code.</para>
55+
</doxygen>)xml");
56+
auto selected = doc.select_node("//*[@id='test-node']");
57+
std::ostringstream os;
58+
ASSERT_TRUE(AppendIfParagraph(os, selected.node()));
59+
EXPECT_EQ(os.str(), "Try using `int f() { return 42; }` in your code.\n");
60+
}
61+
62+
TEST(Doxygen2Markdown, ParagraphWithUnknown) {
63+
pugi::xml_document doc;
64+
doc.load_string(R"xml(<?xml version="1.0" standalone="yes"?>
65+
<doxygen version="1.9.1" xml:lang="en-US">
66+
<para id='test-node'>Uh oh: <itemizedlist></itemizedlist></para>
67+
</doxygen>)xml");
68+
auto selected = doc.select_node("//*[@id='test-node']");
69+
std::ostringstream os;
70+
EXPECT_THROW(AppendIfParagraph(os, selected.node()), std::runtime_error);
71+
}
72+
73+
TEST(Doxygen2Markdown, ParagraphWithUnknownOutput) {
74+
pugi::xml_document doc;
75+
doc.load_string(R"xml(<?xml version="1.0" standalone="yes"?>
76+
<doxygen version="1.9.1" xml:lang="en-US">
77+
<para id='test-node'>Uh oh:
78+
<itemizedlist a1="attr1" a2="attr2">
79+
<listitem>1</listitem>
80+
<listitem>2</listitem>
81+
</itemizedlist></para>
82+
</doxygen>)xml");
83+
auto selected = doc.select_node("//*[@id='test-node']");
84+
std::ostringstream os;
85+
EXPECT_THROW(
86+
try {
87+
AppendIfParagraph(os, selected.node());
88+
} catch (std::runtime_error const& ex) {
89+
EXPECT_THAT(ex.what(), Not(HasSubstr("\n")));
90+
EXPECT_THAT(ex.what(),
91+
HasSubstr("<itemizedlist a1=\"attr1\" a2=\"attr2\">"));
92+
EXPECT_THAT(ex.what(), HasSubstr("<listitem>1</listitem>"));
93+
EXPECT_THAT(ex.what(), HasSubstr("<listitem>2</listitem>"));
94+
EXPECT_THAT(ex.what(), HasSubstr("</itemizedlist>"));
95+
throw;
96+
},
97+
std::runtime_error);
98+
}
99+
100+
} // namespace

vcpkg.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
"protobuf",
2828
"nlohmann-json",
2929
"benchmark",
30-
"gtest"
30+
"gtest",
31+
{
32+
"name": "pugixml",
33+
"$description": "Only used for the docfx feature"
34+
}
3135
]
3236
}

0 commit comments

Comments
 (0)