diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-07-01 21:12:11 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-07-01 21:12:11 +0100 |
| commit | 85baf16dd366fb501dc522a0957ec680dc9478f0 (patch) | |
| tree | 64b9a9463c6b886e9784b1fce9ff5c80dc7e0a23 /nihil.cli/command_tree.ccm | |
| parent | e461c17c24115132601362a96b6a85c0dd12f471 (diff) | |
| download | nihil-85baf16dd366fb501dc522a0957ec680dc9478f0.tar.gz nihil-85baf16dd366fb501dc522a0957ec680dc9478f0.tar.bz2 | |
cli: clean up
Diffstat (limited to 'nihil.cli/command_tree.ccm')
| -rw-r--r-- | nihil.cli/command_tree.ccm | 64 |
1 files changed, 34 insertions, 30 deletions
diff --git a/nihil.cli/command_tree.ccm b/nihil.cli/command_tree.ccm index 6eae9e6..6cfabe9 100644 --- a/nihil.cli/command_tree.ccm +++ b/nihil.cli/command_tree.ccm @@ -30,6 +30,16 @@ struct command_tree_node final { } + // Not copyable. + command_tree_node(command_tree_node const &) = delete; + auto operator=(command_tree_node const &) -> command_tree_node & = delete; + + // Movable. + command_tree_node(command_tree_node &&) = default; + auto operator=(command_tree_node &&) -> command_tree_node & = default; + + ~command_tree_node() = default; + // Return a child node, or NULL if the child doesn't exist. [[nodiscard]] auto get_child(this command_tree_node const &self, std::string_view const child) -> command_tree_node const * @@ -63,7 +73,7 @@ struct command_tree_node final : std::string(child); auto node = command_tree_node(&self, child, std::make_shared<nihil::command>(path)); - auto [it, ok] = self.m_children.emplace(child, node); + auto [it, ok] = self.m_children.emplace(child, std::move(node)); if (!ok) throw std::logic_error("failed to insert command tree node"); return &it->second; @@ -113,41 +123,41 @@ private: struct command_tree { // Add a node to the tree. Returns false if the node already exists. - auto insert(this command_tree &self, std::vector<std::string_view> const &path, + auto insert(this command_tree &self, std::ranges::range auto &&path, std::shared_ptr<command> command) -> void + requires(std::constructible_from<std::string_view, + std::ranges::range_value_t<decltype(path)>>) { auto *this_node = &self.m_root_node; // Find the node for this key. for (auto &&this_word : path) - this_node = this_node->get_or_create_child(this_word); + this_node = this_node->get_or_create_child(std::string_view(this_word)); // Set the new value. this_node->command(std::move(command)); } - // Find a node in the tree. - [[nodiscard]] auto - find(this command_tree const &self, int &argc, char **&argv) -> command_tree_node const * + // Find a node in the tree. Returns the node and the remaining unprocessed arguments. + [[nodiscard]] auto find(this command_tree const &self, std::ranges::range auto &&args) + requires(std::constructible_from<std::string_view, + std::ranges::range_value_t<decltype(args)>>) { auto const *this_node = &self.m_root_node; - // Iterate until we don't find a child command, then return that node. - while (argv[0] != nullptr) { - auto const *next_node = this_node->get_child(argv[0]); - - if (next_node == nullptr) - return this_node; - - this_node = next_node; - - --argc; - ++argv; - } - - // We ran out of path without finding a valid command. Return this - // node; the caller will notice the missing command. - return this_node; + auto rest = + args | std::views::take_while([&](auto &&str) { + auto const *child = this_node->get_child(std::string_view(str)); + if (child == nullptr) + return false; + this_node = child; + return true; + }); + + // Force evaluation of the view, otherwise the reference to this_node + // would be dangling. + auto taken = std::ranges::distance(args) - std::ranges::distance(rest); + return std::make_pair(this_node, args | std::views::drop(taken)); } private: @@ -159,15 +169,9 @@ private: { auto tree = command_tree(); - for (auto &&command : get_registered_commands()) { - auto split_path = command->path() // - | std::views::split(' ') // - | std::views::transform(construct<std::string_view>) // - | std::ranges::to<std::vector>(); - + for (auto &&command : get_registered_commands()) // Throws std::logic_error on duplicates. - tree.insert(split_path, command); - } + tree.insert(command->path() | std::views::split(' '), command); return tree; } |
