ChainCLI
A modern C++20 command-line interface library
Loading...
Searching...
No Matches
command_tree.h
1/*
2 * Copyright 2025 Dominik Czekai
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18#include <iostream>
19#include <memory>
20#include <stdexcept>
21#include <string_view>
22#include <unordered_map>
23
24#include "command.h"
25
26namespace cli::commands
27{
28
30class CommandNotFoundException : public std::runtime_error
31{
32public:
36 CommandNotFoundException(const std::string &missingId, const std::vector<std::string> &chain)
37 : std::runtime_error(buildMessage(missingId, chain)), missing(missingId), path(chain)
38 {
39 }
40
41 const std::string &missingId() const noexcept { return missing; }
42
43 const std::vector<std::string> &insertChain() const noexcept { return path; }
44
45private:
46 std::string missing;
47 std::vector<std::string> path;
48
49 static std::string buildMessage(const std::string &id, const std::vector<std::string> &chain);
50};
51
56{
57public:
60 explicit CommandTree(std::string_view rootName);
61
67 template <typename... Ids>
68 void insert(std::unique_ptr<Command> cmd, std::string_view parentId, Ids &&...rest)
69 {
70 Command *parentCommandPtr = find(parentId, std::forward<Ids>(rest)...);
71 if (!parentCommandPtr)
72 {
73 std::vector<std::string> chain{std::string(parentId), std::string(rest)...};
74 throw CommandNotFoundException(std::string(parentId), chain);
75 }
76
77 parentCommandPtr->withSubCommand(std::move(cmd));
78 }
79
82 void insert(std::unique_ptr<Command> cmd) // insert at root
83 {
84 root->withSubCommand(std::move(cmd));
85 }
86
91 template <typename... Ids> Command *find(Ids &&...ids) const
92 {
93 return findRecursive(root.get(), std::forward<Ids>(ids)...);
94 }
95
99 void forEachCommand(const std::function<void(Command *)> &func) const
100 {
101 if (root)
102 {
103 forEachCommandRecursive(root.get(), func);
104 }
105 }
106
110 void forEachCommand(const std::function<void(Command &)> &func) const
111 {
112 if (root)
113 {
114 forEachCommandRecursive(root.get(), func);
115 }
116 }
117
120 Command *getRootCommand() { return root.get(); }
121
124 const Command *getRootCommand() const { return root.get(); }
125
131 std::string_view getPathForCommand(Command *cmd) const;
132
136 void buildCommandPathMap(const std::string &separator = " ");
137
141 std::vector<Command *> getAllCommands() const;
142
146 std::vector<const Command *> getAllCommandsConst() const;
147
148private:
149 std::unique_ptr<Command> root;
150 std::unordered_map<Command *, std::string> commandPathMap;
151
152 void buildCommandPathMapRecursive(Command *cmd, std::vector<std::string> &path,
153 const std::string &separator);
154
155 // Recursive finder
156 template <typename... Ids>
157 static Command *findRecursive(Command *cmdPtr, std::string_view id, Ids &&...rest)
158 {
159 auto it = cmdPtr->getSubCommands().find(std::string(id));
160 if (it == cmdPtr->getSubCommands().end())
161 return nullptr;
162
163 Command *subCommandPtr = it->second.get();
164
165 if constexpr (sizeof...(rest) == 0)
166 {
167 // Base case: no more ids, return the child
168 return subCommandPtr;
169 }
170 // Recursive case: continue with remaining ids
171 return findRecursive(subCommandPtr, std::forward<Ids>(rest)...);
172 }
173
174 static Command *findRecursive(Command *cmdPtr) { return cmdPtr; }
175
176 // Recursive DFS helper
177 static void forEachCommandRecursive(Command *cmdPtr, const std::function<void(Command *)> &func)
178 {
179 if (cmdPtr)
180 {
181 func(cmdPtr); // call user-provided function
182 for (const auto &[key, subCommandPtr] : cmdPtr->getSubCommands())
183 {
184 forEachCommandRecursive(subCommandPtr.get(), func);
185 }
186 }
187 }
188
189 static void forEachCommandRecursive(Command *cmdPtr, const std::function<void(Command &)> &func)
190 {
191 if (cmdPtr)
192 {
193 func(*cmdPtr); // call user-provided function
194 for (const auto &[key, subCommandPtr] : cmdPtr->getSubCommands())
195 {
196 forEachCommandRecursive(subCommandPtr.get(), func);
197 }
198 }
199 }
200
201 // Helper function to collect all commands into a vector
202 static void getAllCommandsRecursive(Command *cmdPtr, std::vector<Command *> &commands)
203 {
204 if (cmdPtr)
205 {
206 commands.push_back(cmdPtr);
207 for (const auto &[key, subCommandPtr] : cmdPtr->getSubCommands())
208 {
209 getAllCommandsRecursive(subCommandPtr.get(), commands);
210 }
211 }
212 }
213
214 // Helper function to collect all commands into a vector (const version)
215 static void getAllCommandsRecursive(const Command *cmdPtr, std::vector<const Command *> &commands)
216 {
217 if (cmdPtr)
218 {
219 commands.push_back(cmdPtr);
220 for (const auto &[key, subCommandPtr] : cmdPtr->getSubCommands())
221 {
222 getAllCommandsRecursive(subCommandPtr.get(), commands);
223 }
224 }
225 }
226};
227} // namespace cli::commands
Exception thrown when a command is not found in the command tree.
Definition command_tree.h:31
CommandNotFoundException(const std::string &missingId, const std::vector< std::string > &chain)
Construct a new CommandNotFoundException.
Definition command_tree.h:36
Tree structure to manage commands and their subcommands.
Definition command_tree.h:56
void forEachCommand(const std::function< void(Command &)> &func) const
Apply a function to each command in the tree.
Definition command_tree.h:110
void insert(std::unique_ptr< Command > cmd, std::string_view parentId, Ids &&...rest)
Insert a command into the tree.
Definition command_tree.h:68
void buildCommandPathMap(const std::string &separator=" ")
Build a map of command paths for quick lookup.
Definition command_tree.cpp:33
std::string_view getPathForCommand(Command *cmd) const
Get the path for a command in the tree.
Definition command_tree.cpp:28
const Command * getRootCommand() const
Get the root command of the tree.
Definition command_tree.h:124
void forEachCommand(const std::function< void(Command *)> &func) const
Apply a function to each command in the tree.
Definition command_tree.h:99
Command * getRootCommand()
Get the root command of the tree.
Definition command_tree.h:120
std::vector< const Command * > getAllCommandsConst() const
Get a vector of all commands in the tree (const version).
Definition command_tree.cpp:93
void insert(std::unique_ptr< Command > cmd)
Insert a command into the tree.
Definition command_tree.h:82
std::vector< Command * > getAllCommands() const
Get a vector of all commands in the tree.
Definition command_tree.cpp:83
Command * find(Ids &&...ids) const
Find a command in the tree by a path of identifiers leading to it.
Definition command_tree.h:91
Represents a command in the CLI application.
Definition command.h:36
Command & withSubCommand(std::unique_ptr< Command > subCommandPtr)
Add a sub-command to this command.
Definition command.cpp:104
auto & getSubCommands()
Get all sub-commands of the command.
Definition command.h:170