Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tested_by functionality #54

Merged
merged 5 commits into from
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 106 additions & 7 deletions emitters/yaml_base_emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,100 @@ bool yaml_base_emitter::check_scalar(const std::string& filepath,

/**************************************************************************************************/

bool yaml_base_emitter::check_scalar_array(const std::string& filepath,
const json& have_node,
const json& expected_node,
const std::string& nodepath,
json& merged_node,
const std::string& key) {
const auto notify = [&](const std::string& validate_message,
const std::string& update_message) {
check_notify(filepath, nodepath, key, validate_message, update_message);
};

const auto notify_fail = [&](const std::string& message) {
check_notify(filepath, nodepath, key, message, message);
};

if (!expected_node.count(key)) {
throw std::runtime_error("missing expected node?");
}

const json& expected = expected_node[key];

if (!expected.is_string()) {
throw std::runtime_error("expected type mismatch?");
}

const std::string& expected_scalar(expected);
json& result = merged_node[key];

if (!have_node.count(key)) {
if (expected_scalar == tag_value_deprecated_k) {
// deprecated key not present in have. Do nothing, no error.
return false;
} else {
notify("value missing", "value inserted");
result = expected;
return true;
}
}

const json& have = have_node[key];

if (have.is_string()) {
const std::string& have_scalar(have);

if (expected_scalar == tag_value_missing_k && have_scalar == tag_value_missing_k) {
if (_mode == yaml_mode::validate) {
notify("value not documented", "");
}

return true;
}

if (expected_scalar == tag_value_deprecated_k) {
notify("key is deprecated", "deprecated key removed");
return true;
}

if (expected_scalar == tag_value_optional_k && have_scalar == tag_value_optional_k) {
return false;
}

if (hyde::is_tag(have_scalar)) {
notify("value is unexpected tag",
"value updated from `" + have_scalar + "` to `" + expected_scalar + "`");
result = expected; // Replace unexpected tag
return true;
}
}

if (!have.is_array()) {
notify_fail("value not an array; expected an array of scalar values");
return true;
}

result = have;

// We have an array; make sure its elements are scalar
std::size_t index{0};
bool failure{false};
for (const auto& have_element : have) {
if (!have_element.is_string()) {
failure = true;
notify_fail("non-scalar array element at index " + std::to_string(index));
} else if (hyde::is_tag(have_element)) {
failure = true;
notify_fail("invalid value at index " + std::to_string(index));
}
}

return failure;
}

/**************************************************************************************************/

bool yaml_base_emitter::check_object_array(const std::string& filepath,
const json& have_node,
const json& expected_node,
Expand Down Expand Up @@ -404,7 +498,7 @@ bool yaml_base_emitter::check_object_array(const std::string& filepath,
if (have_elements.count(object_key) == 0) {
std::string count_str(std::to_string(count));
std::string message("object at index " + count_str + " has no key");
notify(message + "; skipped", message);
notify(message, message + "; skipped");
// preserve object indices for merging below. Name is irrelevant as
// long as it's unique. Prefix with '.' to prevent actual key
// conflicts.
Expand All @@ -430,16 +524,16 @@ bool yaml_base_emitter::check_object_array(const std::string& filepath,
have_found_iter != have_map.end() && have_found_iter->first == expected_key;
std::string index_str(std::to_string(index));
if (!have_found) {
notify("required object inserted at index " + index_str,
"missing required object at index " + index_str);
notify("missing required object at index " + index_str,
"required object inserted at index " + index_str);
result_array.push_back(expected_object);
failure = true;
} else {
std::size_t have_index = have_found_iter->second;
if (have_index != index) {
std::string have_index_str(std::to_string(have_index));
notify("moved item at index " + have_index_str + " to index " + index_str,
"bad item location; have: " + have_index_str + ", expected: " + index_str);
notify("bad item location; have: " + have_index_str + ", expected: " + index_str,
"moved item at index " + have_index_str + " to index " + index_str);
failure = true;
}
std::string nodepath = "['" + key + "'][" + index_str + "]";
Expand All @@ -455,7 +549,7 @@ bool yaml_base_emitter::check_object_array(const std::string& filepath,
std::string expected_size(std::to_string(expected.size()));
std::string message =
"sequence size mismatch; have " + have_size + ", expected " + expected_size;
notify(message + "; fixed", message);
notify(message, message + "; fixed");
failure = true;
}

Expand Down Expand Up @@ -681,7 +775,8 @@ auto load_yaml(const boost::filesystem::path& path) try {

bool yaml_base_emitter::reconcile(json expected,
boost::filesystem::path root_path,
boost::filesystem::path path) {
boost::filesystem::path path,
json& out_reconciled) {
bool failure{false};

/* begin hack */ {
Expand Down Expand Up @@ -726,6 +821,8 @@ bool yaml_base_emitter::reconcile(json expected,
json merged;

std::tie(failure, merged) = merge(relative_path, have, expected);
out_reconciled = merged;
out_reconciled["documentation_path"] = relative_path;

switch (_mode) {
case hyde::yaml_mode::validate: {
Expand All @@ -745,6 +842,8 @@ bool yaml_base_emitter::reconcile(json expected,
} break;
}
} else { // file does not exist
out_reconciled = expected;

switch (_mode) {
case hyde::yaml_mode::validate: {
std::cerr << relative_path << ": required file does not exist\n";
Expand Down
17 changes: 13 additions & 4 deletions emitters/yaml_base_emitter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@ struct yaml_base_emitter {
public:
yaml_base_emitter(boost::filesystem::path src_root,
boost::filesystem::path dst_root,
yaml_mode mode)
: _src_root(std::move(src_root)), _dst_root(std::move(dst_root)), _mode(mode) {}
yaml_mode mode,
emit_options options)
: _src_root(std::move(src_root)), _dst_root(std::move(dst_root)), _mode(mode), _options(std::move(options)) {}

virtual bool emit(const json& json) = 0;
virtual bool emit(const json& j, json& out_emitted) = 0;

protected:
json base_emitter_node(std::string layout, std::string title, std::string tag);

bool reconcile(json node, boost::filesystem::path root_path, boost::filesystem::path path);
bool reconcile(json node, boost::filesystem::path root_path, boost::filesystem::path path, json& out_reconciled);

std::string defined_in_file(const std::string& src_path,
const boost::filesystem::path& src_root);
Expand All @@ -89,6 +90,13 @@ struct yaml_base_emitter {
json& out_merged,
const std::string& key);

bool check_scalar_array(const std::string& filepath,
const json& have_node,
const json& expected_node,
const std::string& nodepath,
json& merged_node,
const std::string& key);

using check_proc = std::function<bool(const std::string& filepath,
const json& have,
const json& expected,
Expand Down Expand Up @@ -145,6 +153,7 @@ struct yaml_base_emitter {
const boost::filesystem::path _src_root;
const boost::filesystem::path _dst_root;
const yaml_mode _mode;
const emit_options _options;

static file_checker checker_s;
};
Expand Down
35 changes: 35 additions & 0 deletions emitters/yaml_base_emitter_fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ written permission of Adobe.

#pragma once

// stdc++
#include <stdexcept>

/**************************************************************************************************/

namespace hyde {
Expand All @@ -24,6 +27,38 @@ static constexpr char const* index_filename_k = "index.md";

/**************************************************************************************************/

enum class attribute_category {
disabled,
required,
optional,
deprecated
};

static constexpr char const* get_tag(attribute_category c) {
switch (c) {
case attribute_category::required:
return tag_value_missing_k;
case attribute_category::optional:
return tag_value_optional_k;
case attribute_category::deprecated:
return tag_value_deprecated_k;
default:
throw std::invalid_argument("unexpected attribute category");
}
}

static inline bool is_tag(const std::string& s) {
return s.substr(0, 2) == "__";
}

/**************************************************************************************************/

struct emit_options {
attribute_category _tested_by;
};

/**************************************************************************************************/

} // namespace hyde

/**************************************************************************************************/
10 changes: 6 additions & 4 deletions emitters/yaml_class_emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ bool yaml_class_emitter::do_merge(const std::string& filepath,

/**************************************************************************************************/

bool yaml_class_emitter::emit(const json& j) {
bool yaml_class_emitter::emit(const json& j, json& out_emitted) {
json node = base_emitter_node("class", j["name"], "class");
node["defined-in-file"] = defined_in_file(j["defined-in-file"], _src_root);
maybe_annotate(j, node);
Expand Down Expand Up @@ -110,14 +110,16 @@ bool yaml_class_emitter::emit(const json& j) {
auto dst = dst_path(j,
static_cast<const std::string&>(j["name"]));

bool failure = reconcile(std::move(node), _dst_root, std::move(dst) / index_filename_k);
bool failure = reconcile(std::move(node), _dst_root, std::move(dst) / index_filename_k, out_emitted);

const auto& methods = j["methods"];
yaml_function_emitter function_emitter(_src_root, _dst_root, _mode, true);
yaml_function_emitter function_emitter(_src_root, _dst_root, _mode, _options, true);

for (auto it = methods.begin(); it != methods.end(); ++it) {
function_emitter.set_key(it.key());
failure |= function_emitter.emit(it.value());
auto function_emitted = hyde::json::object();
failure |= function_emitter.emit(it.value(), function_emitted);
out_emitted["methods"].push_back(std::move(function_emitted));
}

return failure;
Expand Down
7 changes: 4 additions & 3 deletions emitters/yaml_class_emitter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ namespace hyde {
struct yaml_class_emitter : public yaml_base_emitter {
explicit yaml_class_emitter(boost::filesystem::path src_root,
boost::filesystem::path dst_root,
yaml_mode mode)
: yaml_base_emitter(std::move(src_root), std::move(dst_root), mode) {}
yaml_mode mode,
emit_options options)
: yaml_base_emitter(std::move(src_root), std::move(dst_root), mode, std::move(options)) {}

bool emit(const json& json) override;
bool emit(const json& j, json& out_emitted) override;

bool do_merge(const std::string& filepath,
const json& have,
Expand Down
4 changes: 2 additions & 2 deletions emitters/yaml_enum_emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ bool yaml_enum_emitter::do_merge(const std::string& filepath,

/**************************************************************************************************/

bool yaml_enum_emitter::emit(const json& j) {
bool yaml_enum_emitter::emit(const json& j, json& out_emitted) {
const std::string& name = j["name"];

// Most likely an enum forward declaration. Nothing to document here.
Expand All @@ -65,7 +65,7 @@ bool yaml_enum_emitter::emit(const json& j) {
node["values"].push_back(std::move(cur_value));
}

return reconcile(std::move(node), _dst_root, dst_path(j) / filename);
return reconcile(std::move(node), _dst_root, dst_path(j) / filename, out_emitted);
}

/**************************************************************************************************/
Expand Down
7 changes: 4 additions & 3 deletions emitters/yaml_enum_emitter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ namespace hyde {
struct yaml_enum_emitter : public yaml_base_emitter {
explicit yaml_enum_emitter(boost::filesystem::path src_root,
boost::filesystem::path dst_root,
yaml_mode mode)
: yaml_base_emitter(std::move(src_root), std::move(dst_root), mode) {}
yaml_mode mode,
emit_options options)
: yaml_base_emitter(std::move(src_root), std::move(dst_root), mode, std::move(options)) {}

bool emit(const json& json) override;
bool emit(const json& j, json& out_emitted) override;

bool do_merge(const std::string& filepath,
const json& have,
Expand Down
10 changes: 8 additions & 2 deletions emitters/yaml_function_emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ bool yaml_function_emitter::do_merge(const std::string& filepath,
failure |= check_scalar(filepath, have, expected, nodepath, out_merged, "description");
failure |= check_scalar(filepath, have, expected, nodepath, out_merged, "signature_with_names");
failure |= check_scalar(filepath, have, expected, nodepath, out_merged, "return");
if (_options._tested_by != hyde::attribute_category::disabled) {
failure |= check_scalar_array(filepath, have, expected, nodepath, out_merged, "tested_by");
}
// failure |= check_scalar(filepath, have, expected, nodepath, out_merged,
// "annotation");

Expand Down Expand Up @@ -65,7 +68,7 @@ bool yaml_function_emitter::do_merge(const std::string& filepath,

/**************************************************************************************************/

bool yaml_function_emitter::emit(const json& jsn) {
bool yaml_function_emitter::emit(const json& jsn, json& out_emitted) {
boost::filesystem::path dst;
std::string name;
std::string filename;
Expand Down Expand Up @@ -94,6 +97,9 @@ bool yaml_function_emitter::emit(const json& jsn) {
// description is now optional when there is a singular variant.
overloads[key]["description"] = count > 1 ? tag_value_missing_k : tag_value_optional_k;
overloads[key]["return"] = tag_value_optional_k;
if (_options._tested_by != hyde::attribute_category::disabled) {
overloads[key]["tested_by"] = hyde::get_tag(_options._tested_by);
}
maybe_annotate(overload, overloads[key]);

if (!overload["arguments"].empty()) {
Expand Down Expand Up @@ -122,7 +128,7 @@ bool yaml_function_emitter::emit(const json& jsn) {
if (is_ctor) node["is_ctor"] = true;
if (is_dtor) node["is_dtor"] = true;

return reconcile(std::move(node), _dst_root, dst / (filename + ".md"));
return reconcile(std::move(node), _dst_root, dst / (filename + ".md"), out_emitted);
}

/**************************************************************************************************/
Expand Down
5 changes: 3 additions & 2 deletions emitters/yaml_function_emitter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ struct yaml_function_emitter : public yaml_base_emitter {
explicit yaml_function_emitter(boost::filesystem::path src_root,
boost::filesystem::path dst_root,
yaml_mode mode,
emit_options options,
bool as_methods)
: yaml_base_emitter(std::move(src_root), std::move(dst_root), mode),
: yaml_base_emitter(std::move(src_root), std::move(dst_root), mode, std::move(options)),
_as_methods(as_methods) {}

void set_key(std::string key) { _key = std::move(key); }

bool emit(const json& json) override;
bool emit(const json& j, json& out_emitted) override;

bool do_merge(const std::string& filepath,
const json& have,
Expand Down
Loading