Skip to content

Commit 3e1c279

Browse files
committed
refactor: tool uses Expected
1 parent 1a1b510 commit 3e1c279

File tree

7 files changed

+220
-119
lines changed

7 files changed

+220
-119
lines changed

include/mrdocs/Support/Error.hpp

+109-7
Original file line numberDiff line numberDiff line change
@@ -515,25 +515,97 @@ class Unexpected
515515

516516
template <class E> Unexpected(E) -> Unexpected<E>;
517517

518+
namespace detail
519+
{
520+
template <class T>
521+
constexpr
522+
bool
523+
failed(T const&t)
524+
{
525+
if constexpr (isExpected<std::decay_t<T>>)
526+
{
527+
return !t;
528+
}
529+
else if constexpr (std::same_as<std::decay_t<T>, Error>)
530+
{
531+
return t.failed();
532+
}
533+
else if constexpr (requires (T const& t0) { t0.empty(); })
534+
{
535+
return t.empty();
536+
}
537+
else if constexpr (std::constructible_from<bool, T>)
538+
{
539+
return !t;
540+
}
541+
}
542+
543+
template <class T>
544+
constexpr
545+
decltype(auto)
546+
error(T const& t)
547+
{
548+
if constexpr (isExpected<std::decay_t<T>>)
549+
{
550+
return t.error();
551+
}
552+
else if constexpr (std::same_as<std::decay_t<T>, Error>)
553+
{
554+
return t;
555+
}
556+
else if constexpr (requires (T const& t0) { t0.empty(); })
557+
{
558+
return Error("Empty value");
559+
}
560+
else if constexpr (std::constructible_from<bool, T>)
561+
{
562+
return Error("Invalid value");
563+
}
564+
}
565+
}
566+
518567
#ifndef MRDOCS_TRY
519568
# define MRDOCS_MERGE_(a, b) a##b
520569
# define MRDOCS_LABEL_(a) MRDOCS_MERGE_(expected_result_, a)
521570
# define MRDOCS_UNIQUE_NAME MRDOCS_LABEL_(__LINE__)
571+
572+
/// Try to retrive expected-like type
522573
# define MRDOCS_TRY_VOID(expr) \
523574
auto MRDOCS_UNIQUE_NAME = expr; \
524-
if (!MRDOCS_UNIQUE_NAME) { \
525-
return Unexpected(MRDOCS_UNIQUE_NAME.error()); \
575+
if (detail::failed(MRDOCS_UNIQUE_NAME)) { \
576+
return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \
526577
} \
527578
void(0)
528579
# define MRDOCS_TRY_VAR(var, expr) \
529580
auto MRDOCS_UNIQUE_NAME = expr; \
530-
if (!MRDOCS_UNIQUE_NAME) { \
531-
return Unexpected(MRDOCS_UNIQUE_NAME.error()); \
532-
} \
581+
if (detail::failed(MRDOCS_UNIQUE_NAME)) { \
582+
return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \
583+
} \
533584
var = *std::move(MRDOCS_UNIQUE_NAME)
534-
# define GET_MACRO(_1, _2, NAME, ...) NAME
585+
# define MRDOCS_TRY_MSG(var, expr, msg) \
586+
auto MRDOCS_UNIQUE_NAME = expr; \
587+
if (detail::failed(MRDOCS_UNIQUE_NAME)) { \
588+
return Unexpected(Error(msg)); \
589+
} \
590+
var = *std::move(MRDOCS_UNIQUE_NAME)
591+
# define MRDOCS_TRY_GET_MACRO(_1, _2, _3, NAME, ...) NAME
535592
# define MRDOCS_TRY(...) \
536-
GET_MACRO(__VA_ARGS__, MRDOCS_TRY_VAR, MRDOCS_TRY_VOID)(__VA_ARGS__)
593+
MRDOCS_TRY_GET_MACRO(__VA_ARGS__, MRDOCS_TRY_MSG, MRDOCS_TRY_VAR, MRDOCS_TRY_VOID)(__VA_ARGS__)
594+
595+
/// Check existing expected-like type
596+
# define MRDOCS_CHECK_VOID(var) \
597+
if (!detail::failed(var)) { \
598+
return Unexpected(detail::error(var)); \
599+
} \
600+
void(0)
601+
# define MRDOCS_CHECK_MSG(var, msg) \
602+
if (detail::failed(var)) { \
603+
return Unexpected(Error(msg)); \
604+
} \
605+
void(0)
606+
# define MRDOCS_CHECK_GET_MACRO(_1, _2, NAME, ...) NAME
607+
# define MRDOCS_CHECK(...) \
608+
MRDOCS_CHECK_GET_MACRO(__VA_ARGS__, MRDOCS_CHECK_MSG, MRDOCS_CHECK_VOID)(__VA_ARGS__)
537609
#endif
538610

539611

@@ -1854,6 +1926,36 @@ class Expected<T, E>
18541926
: unex_(std::move(u).error()), has_value_(false)
18551927
{ }
18561928

1929+
// The following constructors are extensions that allow
1930+
// Expected to be constructed directly from an error type
1931+
// if this is not ambiguous with the value type. This
1932+
// is not part of std::expected. MRDOCS_EXPECTED_FROM_ERROR
1933+
// can be defined to disable this behavior.
1934+
#ifndef MRDOCS_EXPECTED_FROM_ERROR
1935+
template <class G = E>
1936+
requires
1937+
std::is_constructible_v<E, const G&> &&
1938+
(!std::is_constructible_v<T, const G&>)
1939+
constexpr
1940+
explicit(!std::is_convertible_v<const G&, E>)
1941+
Expected(const G& u)
1942+
noexcept(std::is_nothrow_constructible_v<E, const G&>)
1943+
: unex_(u)
1944+
, has_value_(false)
1945+
{ }
1946+
1947+
template <class G = E>
1948+
requires
1949+
std::is_constructible_v<E, G> &&
1950+
(!std::is_constructible_v<T, G>)
1951+
constexpr
1952+
explicit(!std::is_convertible_v<G, E>)
1953+
Expected(G&& u)
1954+
noexcept(std::is_nothrow_constructible_v<E, G>)
1955+
: unex_(std::move(u)), has_value_(false)
1956+
{ }
1957+
#endif
1958+
18571959
constexpr explicit
18581960
Expected(std::in_place_t) noexcept
18591961
: Expected()

include/mrdocs/Support/Path.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ isDirsy(
175175
176176
@li Separators made uniform
177177
178+
@li Separators are replaced with the native separator
179+
178180
@return The normalized path.
179181
180182
@param pathName The relative or absolute path.

src/lib/Support/Path.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ makeDirsy(
212212
namespace path = llvm::sys::path;
213213

214214
std::string result = static_cast<std::string>(pathName);
215-
if( ! result.empty() &&
216-
! path::is_separator(
215+
if (!result.empty() &&
216+
!path::is_separator(
217217
result.back(),
218218
path::Style::windows_slash))
219219
{

src/tool/Addons.cpp

+31-43
Original file line numberDiff line numberDiff line change
@@ -17,59 +17,47 @@
1717
namespace clang {
1818
namespace mrdocs {
1919

20-
Error
20+
Expected<void>
2121
setupAddonsDir(
2222
llvm::cl::opt<std::string>& addonsDirArg,
2323
char const* argv0,
2424
void* addressOfMain)
2525
{
26-
namespace fs = llvm::sys::fs;
27-
using Process = llvm::sys::Process;
28-
29-
std::string addonsDir;
30-
31-
// Set addons dir
32-
if(! addonsDirArg.getValue().empty())
26+
// Set addons dir from command line argument
27+
if (!addonsDirArg.getValue().empty())
3328
{
34-
// from command line
35-
auto absPath = files::makeAbsolute(
36-
addonsDirArg.getValue());
37-
if(! absPath)
38-
return absPath.error();
39-
addonsDir = files::makeDirsy(files::normalizePath(*absPath));
40-
if(auto err = files::requireDirectory(addonsDir))
41-
return err;
29+
MRDOCS_TRY(
30+
std::string absPath,
31+
files::makeAbsolute(addonsDirArg.getValue()));
32+
std::string addonsDir = files::makeDirsy(
33+
files::normalizePath(absPath));
34+
MRDOCS_TRY(files::requireDirectory(addonsDir));
4235
addonsDirArg.getValue() = addonsDir;
36+
return {};
4337
}
44-
else
45-
{
46-
// check process working directory
47-
addonsDir = fs::getMainExecutable(argv0, addressOfMain);
48-
if(addonsDir.empty())
49-
return Error("getMainExecutable failed");
50-
addonsDir = files::makeDirsy(files::appendPath(
51-
files::getParentDir(addonsDir), "addons"));
52-
if(! files::requireDirectory(addonsDir).failed())
53-
{
54-
// from directory containing the process executable
55-
addonsDirArg.getValue() = addonsDir;
56-
}
57-
else
58-
{
59-
auto addonsEnvVar = Process::GetEnv("MRDOCS_ADDONS_DIR");
60-
if(! addonsEnvVar.has_value())
61-
return Error("no MRDOCS_ADDONS_DIR in environment");
6238

63-
// from environment variable
64-
addonsDir = files::makeDirsy(files::normalizePath(*addonsEnvVar));
65-
if(auto err = files::requireAbsolute(addonsDir))
66-
return err;
67-
if(auto err = files::requireDirectory(addonsDir))
68-
return err;
69-
addonsDirArg.getValue() = addonsDir;
70-
}
39+
// Set addons dir from process working directory
40+
std::string addonsDir = llvm::sys::fs::getMainExecutable(argv0, addressOfMain);
41+
MRDOCS_CHECK(addonsDir, "getMainExecutable failed");
42+
addonsDir = files::makeDirsy(files::appendPath(
43+
files::getParentDir(addonsDir), "addons"));
44+
Error err = files::requireDirectory(addonsDir);
45+
if (!err.failed())
46+
{
47+
addonsDirArg.getValue() = addonsDir;
48+
return {};
7149
}
72-
return Error::success();
50+
51+
// Set addons dir from environment variable
52+
MRDOCS_TRY(
53+
std::string addonsEnvVar,
54+
llvm::sys::Process::GetEnv("MRDOCS_ADDONS_DIR"),
55+
"no MRDOCS_ADDONS_DIR in environment");
56+
addonsDir = files::makeDirsy(files::normalizePath(addonsEnvVar));
57+
MRDOCS_TRY(files::requireAbsolute(addonsDir));
58+
MRDOCS_TRY(files::requireDirectory(addonsDir));
59+
addonsDirArg.getValue() = addonsDir;
60+
return {};
7361
}
7462

7563
} // mrdocs

src/tool/Addons.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ namespace mrdocs {
2020

2121
/** Set the addons directory using the argument as a hint.
2222
23+
24+
2325
@return The error if any occurred.
2426
*/
25-
Error
27+
Expected<void>
2628
setupAddonsDir(
2729
llvm::cl::opt<std::string>& addonsDirArg,
2830
char const* argv0,

src/tool/GenerateAction.cpp

+43-45
Original file line numberDiff line numberDiff line change
@@ -22,83 +22,81 @@
2222
namespace clang {
2323
namespace mrdocs {
2424

25-
Error
25+
Expected<void>
2626
DoGenerateAction()
2727
{
28-
auto& generators = getGenerators();
29-
30-
ThreadPool threadPool(toolArgs.concurrency);
31-
32-
// Calculate additional YAML settings from command line options.
28+
// Get additional YAML settings from command line
3329
std::string extraYaml;
3430
{
3531
llvm::raw_string_ostream os(extraYaml);
36-
if(toolArgs.ignoreMappingFailures.getValue())
32+
if (toolArgs.ignoreMappingFailures.getValue())
33+
{
3734
os << "ignore-failures: true\n";
35+
}
3836
}
3937

38+
// Load YAML configuration file
4039
std::shared_ptr<ConfigImpl const> config;
40+
ThreadPool threadPool(toolArgs.concurrency);
4141
{
42-
// Load configuration file
43-
if(toolArgs.configPath.empty())
44-
return formatError("the config path argument is missing");
45-
auto configFile = loadConfigFile(
42+
MRDOCS_CHECK(toolArgs.configPath, "The config path argument is missing");
43+
MRDOCS_TRY(auto configFile, loadConfigFile(
4644
toolArgs.configPath,
4745
toolArgs.addonsDir,
4846
extraYaml,
4947
nullptr,
50-
threadPool);
51-
if(! configFile)
52-
return configFile.error();
53-
54-
config = std::move(configFile.value());
48+
threadPool));
49+
config = std::move(configFile);
5550
}
5651

57-
58-
// Create the generator
59-
auto generator = generators.find(config->settings().generate);
60-
if(! generator)
61-
return formatError("the Generator \"{}\" was not found",
62-
config->settings().generate);
52+
// Get the generator
53+
MRDOCS_TRY(
54+
Generator const& generator,
55+
getGenerators().find(config->settings().generate),
56+
formatError(
57+
"the Generator \"{}\" was not found",
58+
config->settings().generate));
6359

6460
// Load the compilation database
65-
if(toolArgs.inputPaths.empty())
66-
return formatError("the compilation database path argument is missing");
67-
if(toolArgs.inputPaths.size() > 1)
68-
return formatError("got {} input paths where 1 was expected", toolArgs.inputPaths.size());
61+
MRDOCS_CHECK(toolArgs.inputPaths, "The compilation database path argument is missing");
62+
MRDOCS_CHECK(toolArgs.inputPaths.size() == 1,
63+
formatError(
64+
"got {} input paths where 1 was expected",
65+
toolArgs.inputPaths.size()));
6966
auto compilationsPath = files::normalizePath(toolArgs.inputPaths.front());
7067
std::string errorMessage;
71-
auto jsonCompilations = tooling::JSONCompilationDatabase::loadFromFile(
72-
compilationsPath, errorMessage, tooling::JSONCommandLineSyntax::AutoDetect);
73-
if(! jsonCompilations)
74-
return Error(std::move(errorMessage));
68+
MRDOCS_TRY_MSG(
69+
auto& jsonCompilations,
70+
tooling::JSONCompilationDatabase::loadFromFile(
71+
compilationsPath,
72+
errorMessage,
73+
tooling::JSONCommandLineSyntax::AutoDetect),
74+
std::move(errorMessage));
7575

7676
// Calculate the working directory
77-
auto absPath = files::makeAbsolute(compilationsPath);
78-
if(! absPath)
79-
return absPath.error();
80-
auto workingDir = files::getParentDir(*absPath);
77+
MRDOCS_TRY(auto absPath, files::makeAbsolute(compilationsPath));
78+
auto workingDir = files::getParentDir(absPath);
8179

82-
// normalize outputPath
83-
if( toolArgs.outputPath.empty())
84-
return formatError("output path is empty");
80+
// Normalize outputPath
81+
MRDOCS_CHECK(toolArgs.outputPath, "The output path argument is missing");
8582
toolArgs.outputPath = files::normalizePath(
8683
files::makeAbsolute(toolArgs.outputPath,
8784
(*config)->workingDir));
8885

8986
// Convert relative paths to absolute
9087
AbsoluteCompilationDatabase compilations(
91-
workingDir, *jsonCompilations, config);
88+
workingDir, jsonCompilations, config);
9289

93-
// Run the tool, this can take a while
94-
auto corpus = CorpusImpl::build(
95-
report::Level::info, config, compilations);
96-
if(! corpus)
97-
return formatError("CorpusImpl::build returned \"{}\"", corpus.error());
90+
// Run the tool: this can take a while
91+
MRDOCS_TRY(
92+
auto corpus,
93+
CorpusImpl::build(
94+
report::Level::info, config, compilations));
9895

99-
// Run the generator.
96+
// Run the generator
10097
report::info("Generating docs\n");
101-
return generator->build(toolArgs.outputPath.getValue(), **corpus);
98+
MRDOCS_TRY(generator.build(toolArgs.outputPath.getValue(), *corpus));
99+
return {};
102100
}
103101

104102
} // mrdocs

0 commit comments

Comments
 (0)