Validate data paths and schema paths
- a leaf can be only present as a last item
- a leaf-list can be only present as a last item
- a list (without keys) cannot be present in a dataPath unless it's the
last node
Change-Id: I7c28d5b235fd050e218aa2152e3a863d8db78680
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index 318aada..0d8a606 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -115,6 +115,62 @@
{
}
+namespace {
+template <typename T, typename U>
+auto findFirstOf(const std::vector<U>& nodes)
+{
+ return std::find_if(nodes.begin(), nodes.end(), [](const auto& e) {
+ return std::holds_alternative<T>(e.m_suffix);
+ });
+}
+
+template <typename T>
+void validatePathNodes(const std::vector<T>& nodes)
+{
+ static_assert(std::is_same<T, dataNode_>() || std::is_same<T, schemaNode_>());
+
+ if (nodes.empty()) {
+ // there are default ctors, so it makes sense to specify the same thing via explicit args and not fail
+ return;
+ }
+
+ if (auto firstLeaf = findFirstOf<leaf_>(nodes);
+ firstLeaf != nodes.end() && firstLeaf != nodes.end() - 1) {
+ throw std::logic_error{"Cannot put any extra nodes after a leaf"};
+ }
+
+ if (auto firstLeafList = findFirstOf<leafList_>(nodes);
+ firstLeafList != nodes.end() && firstLeafList != nodes.end() - 1) {
+ throw std::logic_error{"Cannot put any extra nodes after a leaf-list"};
+ }
+
+ if constexpr (std::is_same<T, dataNode_>()) {
+ if (auto firstLeafListElements = findFirstOf<leafListElement_>(nodes);
+ firstLeafListElements != nodes.end() && firstLeafListElements != nodes.end() - 1) {
+ throw std::logic_error{"Cannot put any extra nodes after a leaf-list with element specification"};
+ }
+ if (auto firstList = findFirstOf<list_>(nodes);
+ firstList != nodes.end() && firstList != nodes.end() - 1) {
+ throw std::logic_error{
+ "A list with no key specification can be present only as a last item in a dataPath. Did you mean to use a schemaPath?"
+ };
+ }
+ }
+}
+}
+
+schemaPath_::schemaPath_()
+{
+}
+
+schemaPath_::schemaPath_(const Scope scope, const std::vector<schemaNode_>& nodes, const TrailingSlash trailingSlash)
+ : m_scope(scope)
+ , m_nodes(nodes)
+ , m_trailingSlash(trailingSlash)
+{
+ validatePathNodes(m_nodes);
+}
+
bool schemaPath_::operator==(const schemaPath_& b) const
{
if (this->m_nodes.size() != b.m_nodes.size())
@@ -122,6 +178,18 @@
return this->m_nodes == b.m_nodes;
}
+dataPath_::dataPath_()
+{
+}
+
+dataPath_::dataPath_(const Scope scope, const std::vector<dataNode_>& nodes, const TrailingSlash trailingSlash)
+ : m_scope(scope)
+ , m_nodes(nodes)
+ , m_trailingSlash(trailingSlash)
+{
+ validatePathNodes(m_nodes);
+}
+
bool dataPath_::operator==(const dataPath_& b) const
{
if (this->m_nodes.size() != b.m_nodes.size())
@@ -276,9 +344,11 @@
void schemaPath_::pushFragment(const schemaNode_& fragment)
{
impl_pushFragment(m_nodes, fragment);
+ validatePathNodes(m_nodes);
}
void dataPath_::pushFragment(const dataNode_& fragment)
{
impl_pushFragment(m_nodes, fragment);
+ validatePathNodes(m_nodes);
}
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index 91268e6..83727ff 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -113,6 +113,8 @@
};
struct schemaPath_ {
+ schemaPath_();
+ schemaPath_(const Scope scope, const std::vector<schemaNode_>& nodes, const TrailingSlash trailingSlash = TrailingSlash::NonPresent);
bool operator==(const schemaPath_& b) const;
Scope m_scope = Scope::Relative;
std::vector<schemaNode_> m_nodes;
@@ -122,6 +124,8 @@
};
struct dataPath_ {
+ dataPath_();
+ dataPath_(const Scope scope, const std::vector<dataNode_>& nodes, const TrailingSlash trailingSlash = TrailingSlash::NonPresent);
bool operator==(const dataPath_& b) const;
Scope m_scope = Scope::Relative;
std::vector<dataNode_> m_nodes;