srctree

Robin Linden parent 4c15e72b d0bdc71e
dom: Move nodes_by_xpath into the header

This is required to adapt it to work with other node types.

inlinesplit
dom/dom.cpp added: 47, removed: 51, total 0
@@ -6,13 +6,10 @@
 
#include "util/overloaded.h"
 
#include <algorithm>
#include <iterator>
#include <ostream>
#include <sstream>
#include <utility>
#include <variant>
#include <vector>
 
namespace dom {
namespace {
@@ -42,48 +39,4 @@ std::string to_string(Document const &document) {
return std::move(ss).str();
}
 
// https://developer.mozilla.org/en-US/docs/Web/XPath
// https://en.wikipedia.org/wiki/XPath
std::vector<Element const *> nodes_by_xpath(Element const &root, std::string_view xpath) {
std::vector<Element const *> next_search{&root};
std::vector<Element const *> searching{};
std::vector<Element const *> goal_nodes{};
 
// We only support xpaths in the form /a/b/c right now.
if (!xpath.starts_with('/')) {
return {};
}
xpath.remove_prefix(1);
 
while (!next_search.empty()) {
searching.swap(next_search);
next_search.clear();
 
for (auto node : searching) {
if (xpath == node->name) {
goal_nodes.push_back(node);
continue;
}
 
if (xpath.starts_with(node->name + "/")) {
for (auto const &child : node->children) {
if (auto const *element = std::get_if<Element>(&child)) {
next_search.push_back(element);
}
}
}
}
 
// Remove name + separator.
std::size_t separator_position{xpath.find_first_of("/")};
if (separator_position == xpath.npos) {
break;
}
 
xpath.remove_prefix(separator_position + 1);
}
 
return goal_nodes;
}
 
} // namespace dom
 
dom/dom.h added: 47, removed: 51, total 0
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: 2021 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2021-2023 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
#ifndef DOM_DOM_H_
#define DOM_DOM_H_
 
#include <cstddef>
#include <map>
#include <string>
#include <string_view>
@@ -39,7 +40,49 @@ struct Document {
[[nodiscard]] bool operator==(Document const &) const = default;
};
 
std::vector<Element const *> nodes_by_xpath(Element const &, std::string_view);
// https://developer.mozilla.org/en-US/docs/Web/XPath
// https://en.wikipedia.org/wiki/XPath
inline std::vector<Element const *> nodes_by_xpath(Element const &root, std::string_view xpath) {
std::vector<Element const *> next_search{&root};
std::vector<Element const *> searching{};
std::vector<Element const *> goal_nodes{};
 
// We only support xpaths in the form /a/b/c right now.
if (!xpath.starts_with('/')) {
return {};
}
xpath.remove_prefix(1);
 
while (!next_search.empty()) {
searching.swap(next_search);
next_search.clear();
 
for (auto node : searching) {
if (xpath == node->name) {
goal_nodes.push_back(node);
continue;
}
 
if (xpath.starts_with(node->name + "/")) {
for (auto const &child : node->children) {
if (auto const *element = std::get_if<Element>(&child)) {
next_search.push_back(element);
}
}
}
}
 
// Remove name + separator.
std::size_t separator_position{xpath.find_first_of("/")};
if (separator_position == xpath.npos) {
break;
}
 
xpath.remove_prefix(separator_position + 1);
}
 
return goal_nodes;
}
 
std::string to_string(Document const &node);