Skip to content

Commit 0faf7a9

Browse files
committed
feat: config YAML as dom::Object
1 parent 9c0071a commit 0faf7a9

File tree

7 files changed

+204
-122
lines changed

7 files changed

+204
-122
lines changed

include/mrdocs/Config.hpp

+14-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <mrdocs/Platform.hpp>
1616
#include <mrdocs/Support/Error.hpp>
17+
#include <mrdocs/Dom/Object.hpp>
1718
#include <functional>
1819
#include <memory>
1920
#include <string>
@@ -68,8 +69,11 @@ class MRDOCS_DECL
6869

6970
/** Full path to the working directory
7071
71-
The working directory is used to calculate
72-
full paths from relative paths.
72+
The working directory is the directory
73+
of the mrdocs.yml file.
74+
75+
It is used to calculate full paths
76+
from relative paths.
7377
7478
This string will always be native style
7579
and have a trailing directory separator.
@@ -124,6 +128,14 @@ class MRDOCS_DECL
124128
*/
125129
virtual Settings const& settings() const noexcept = 0;
126130

131+
/** Return a DOM object representing the configuration keys.
132+
133+
The object is invalidated when the configuration
134+
is moved or destroyed.
135+
136+
*/
137+
virtual dom::Object const& object() const = 0;
138+
127139
/// @copydoc settings()
128140
constexpr Settings const*
129141
operator->() const noexcept
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Declared in header `<{{#unless @root.config.baseURL}}{{dcl.file}}{{else}}{{@root.config.baseURL}}{{dcl.file}}#L{{dcl.line}}[{{dcl.file}},window=blank_]{{/unless}}>`
1+
Declared in header `<{{#unless @root.config.base-url}}{{dcl.file}}{{else}}{{@root.config.base-url}}{{dcl.file}}#L{{dcl.line}}[{{dcl.file}},window=blank_]{{/unless}}>`

src/lib/Gen/adoc/Builder.cpp

+1-100
Original file line numberDiff line numberDiff line change
@@ -179,104 +179,6 @@ getRelPrefix(std::size_t depth)
179179
return rel_prefix;
180180
}
181181

182-
class ConfigObjectImpl : public dom::ObjectImpl
183-
{
184-
Config const* config_;
185-
186-
public:
187-
~ConfigObjectImpl() override = default;
188-
189-
ConfigObjectImpl(Config const& config)
190-
: config_(&config)
191-
{}
192-
193-
char const* type_key() const noexcept override
194-
{
195-
return "ConfigObject";
196-
}
197-
198-
dom::Value get(std::string_view key) const override
199-
{
200-
if (key == "multiPage") return (*config_)->multiPage;
201-
if (key == "generate") return (*config_)->generate;
202-
if (key == "workingDir") return (*config_)->workingDir;
203-
auto* config_impl = dynamic_cast<ConfigImpl const*>(config_);
204-
if (config_impl)
205-
{
206-
if (key == "baseURL") return (*config_impl)->baseURL;
207-
if (key == "inaccessibleBases") return (*config_impl)->inaccessibleBases;
208-
if (key == "inaccessibleMembers") return (*config_impl)->inaccessibleMembers;
209-
if (key == "anonymousNamespaces") return (*config_impl)->anonymousNamespaces;
210-
if (key == "ignoreFailures") return (*config_impl)->ignoreFailures;
211-
if (key == "defines") {
212-
dom::Array defines;
213-
for (auto& define: (*config_impl)->defines)
214-
{
215-
defines.emplace_back(define);
216-
}
217-
return defines;
218-
}
219-
}
220-
return {};
221-
}
222-
223-
void set(dom::String key, dom::Value value) override
224-
{
225-
// Cannot set values in the config object from templates
226-
}
227-
228-
bool
229-
visit(std::function<bool(dom::String, dom::Value)> fn) const override
230-
{
231-
if (!fn("multiPage", (*config_)->multiPage)) { return false; };
232-
if (!fn("generate", (*config_)->generate)) { return false; };
233-
if (!fn("workingDir", (*config_)->workingDir)) { return false; };
234-
auto* config_impl = dynamic_cast<ConfigImpl const*>(config_);
235-
if (config_impl)
236-
{
237-
if (!fn("baseURL", (*config_impl)->baseURL)) { return false; };
238-
if (!fn("inaccessibleBases", (*config_impl)->inaccessibleBases)) { return false; };
239-
if (!fn("inaccessibleMembers", (*config_impl)->inaccessibleMembers)) { return false; };
240-
if (!fn("anonymousNamespaces", (*config_impl)->anonymousNamespaces)) { return false; };
241-
if (!fn("ignoreFailures", (*config_impl)->ignoreFailures)) { return false; };
242-
dom::Array defines;
243-
for (auto& define: (*config_impl)->defines)
244-
{
245-
defines.emplace_back(define);
246-
}
247-
if (!fn("defines", defines)) { return false; };
248-
}
249-
return true;
250-
}
251-
252-
/** Return the number of properties in the object.
253-
*/
254-
std::size_t size() const override {
255-
return 9;
256-
};
257-
258-
/** Determine if a key exists.
259-
*/
260-
bool exists(std::string_view key) const override
261-
{
262-
if (key == "multiPage") return true;
263-
if (key == "generate") return true;
264-
if (key == "workingDir") return true;
265-
auto* config_impl = dynamic_cast<ConfigImpl const*>(config_);
266-
if (config_impl)
267-
{
268-
if (key == "baseURL") return true;
269-
if (key == "inaccessibleBases") return true;
270-
if (key == "inaccessibleMembers") return true;
271-
if (key == "anonymousNamespaces") return true;
272-
if (key == "ignoreFailures") return true;
273-
if (key == "defines") return true;
274-
}
275-
return false;
276-
}
277-
};
278-
279-
280182
dom::Value
281183
Builder::
282184
createContext(
@@ -287,8 +189,7 @@ createContext(
287189
domCorpus.get(I.id));
288190
props.emplace_back("relfileprefix",
289191
getRelPrefix(I.Namespace.size()));
290-
props.emplace_back("config",
291-
dom::newObject<ConfigObjectImpl>(domCorpus->config));
192+
props.emplace_back("config", domCorpus->config.object());
292193
return dom::Object(std::move(props));
293194
}
294195

src/lib/Lib/Config.cpp

+1-5
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,10 @@
99
// Official repository: https://github.com/cppalliance/mrdocs
1010
//
1111

12-
#include "lib/Lib/ConfigImpl.hpp"
13-
#include "lib/Support/Path.hpp"
12+
#include "mrdocs/Config.hpp"
1413
#include <mrdocs/Support/Error.hpp>
1514
#include <llvm/Config/llvm-config.h>
1615
#include <llvm/Support/FileSystem.h>
17-
#include <llvm/Support/Path.h>
18-
#include <llvm/Support/YAMLParser.h>
19-
#include <llvm/Support/YAMLTraits.h>
2016

2117
#include <version>
2218

src/lib/Lib/ConfigImpl.cpp

+141
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ parseSymbolFilter(
139139
root.mergePattern(parts, excluded);
140140
}
141141

142+
dom::Object
143+
toDomObject(std::string_view configYaml);
144+
142145
} // (anon)
143146

144147
ConfigImpl::
@@ -174,6 +177,7 @@ ConfigImpl(
174177
// Config strings
175178
settings_.configYaml = configYaml;
176179
settings_.extraYaml = extraYaml;
180+
configObj_ = toDomObject(settings_.configYaml);
177181

178182
// Parse the YAML strings
179183
YamlReporter reporter;
@@ -338,5 +342,142 @@ loadConfigFile(
338342
threadPool);
339343
}
340344

345+
namespace {
346+
dom::Value
347+
toDom(llvm::yaml::Node* Value);
348+
349+
dom::Object
350+
toDomObject(llvm::yaml::MappingNode* Object)
351+
{
352+
dom::Object obj;
353+
for (auto &Pair : *Object)
354+
{
355+
auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(Pair.getKey());
356+
if (!KeyString) { continue; }
357+
SmallString<10> KeyStorage;
358+
StringRef KeyValue = KeyString->getValue(KeyStorage);
359+
llvm::yaml::Node *Value = Pair.getValue();
360+
if (!Value) {
361+
obj.set(KeyValue, dom::Kind::Undefined);
362+
continue;
363+
}
364+
dom::Value value = toDom(Value);
365+
obj.set(KeyValue, value);
366+
}
367+
return obj;
368+
}
369+
370+
dom::Array
371+
toDomArray(llvm::yaml::SequenceNode* Array)
372+
{
373+
dom::Array arr;
374+
for (auto &Node : *Array)
375+
{
376+
dom::Value value = toDom(&Node);
377+
arr.push_back(value);
378+
}
379+
return arr;
380+
}
381+
382+
dom::Value
383+
toDomScalar(llvm::yaml::ScalarNode* Scalar)
384+
{
385+
SmallString<10> ScalarStorage;
386+
StringRef ScalarValue = Scalar->getValue(ScalarStorage);
387+
StringRef RawValue = Scalar->getRawValue();
388+
bool const isEscaped = RawValue.size() != ScalarValue.size();
389+
if (isEscaped)
390+
{
391+
return ScalarValue;
392+
}
393+
std::int64_t integer;
394+
auto res = std::from_chars(
395+
ScalarValue.begin(),
396+
ScalarValue.end(),
397+
integer);
398+
if (res.ec == std::errc())
399+
{
400+
return integer;
401+
}
402+
bool const isBool = ScalarValue == "true" || ScalarValue == "false";
403+
if (isBool)
404+
{
405+
return ScalarValue == "true";
406+
}
407+
bool const isNull = ScalarValue == "null";
408+
if (isNull)
409+
{
410+
return nullptr;
411+
}
412+
return ScalarValue;
413+
}
414+
415+
dom::Value
416+
toDom(llvm::yaml::Node* Value)
417+
{
418+
auto *ValueObject = dyn_cast<llvm::yaml::MappingNode>(Value);
419+
if (ValueObject)
420+
{
421+
return toDomObject(ValueObject);
422+
}
423+
auto *ValueArray = dyn_cast<llvm::yaml::SequenceNode>(Value);
424+
if (ValueArray)
425+
{
426+
return toDomArray(ValueArray);
427+
}
428+
auto *ValueString = dyn_cast<llvm::yaml::ScalarNode>(Value);
429+
if (ValueString)
430+
{
431+
return toDomScalar(ValueString);
432+
}
433+
return nullptr;
434+
}
435+
436+
/* Convert a YAML string to a DOM object.
437+
438+
YAML forbids tab characters to use as indentation so
439+
only some JSON files are valid YAML.
440+
441+
Also instead of providing built-in support for
442+
types such as `bool` or `int`, YAML uses strings
443+
for everything, which the specification defines
444+
as "scalar" values.
445+
446+
When converting a scalar to a DOM value, only
447+
escaped strings are preserved as strings.
448+
Unescaped strings are converted to numbers
449+
if possible, and then to booleans if possible.
450+
This is done to preserve compatibility with
451+
JSON, allow the user to specify scalars as
452+
boolean or integer values, match the original
453+
intent of the author, and for scalar values
454+
to interoperate with other handlebars templates.
455+
456+
*/
457+
dom::Object
458+
toDomObject(std::string_view yaml)
459+
{
460+
llvm::SourceMgr SM;
461+
llvm::yaml::Stream YAMLStream_(yaml, SM);
462+
llvm::yaml::document_iterator I = YAMLStream_.begin();
463+
if (I == YAMLStream_.end())
464+
{
465+
return {};
466+
}
467+
llvm::yaml::Node *Root = I->getRoot();
468+
auto *Object = dyn_cast<llvm::yaml::MappingNode>(Root);
469+
if (!Object)
470+
{
471+
return {};
472+
}
473+
return toDomObject(Object);
474+
}
475+
} // (anon)
476+
477+
dom::Object const&
478+
ConfigImpl::object() const {
479+
return configObj_;
480+
}
481+
341482
} // mrdocs
342483
} // clang

src/lib/Lib/ConfigImpl.hpp

+13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
namespace clang {
2525
namespace mrdocs {
2626

27+
/* Private configuration implementation.
28+
29+
This class is used internally to hold the
30+
configuration settings. It is not part of
31+
the public API and plugins should not use
32+
it.
33+
34+
*/
2735
class ConfigImpl
2836
: public Config
2937
, public std::enable_shared_from_this<ConfigImpl>
@@ -142,11 +150,16 @@ class ConfigImpl
142150
return &settings_;
143151
}
144152

153+
/// @copydoc Config::object()
154+
dom::Object const&
155+
object() const override;
156+
145157
private:
146158
SettingsImpl settings_;
147159
ThreadPool& threadPool_;
148160
llvm::SmallString<0> outputPath_;
149161
std::vector<std::string> inputFileIncludes_;
162+
dom::Object configObj_;
150163

151164
friend class Config;
152165
friend class Options;

0 commit comments

Comments
 (0)