MaCh3  2.6.1
Reference Guide
MaCh3Program.cpp
Go to the documentation of this file.
1 
4 #include <dlfcn.h>
5 #include <filesystem>
6 #include <iostream>
7 #include <memory>
8 #include <sstream>
9 #include "Manager/MaCh3Logger.h"
10 #include "CLI/MaCh3Program.hpp"
11 
12 using std::vector, std::string, std::map;
13 namespace fs = std::filesystem;
14 
15 namespace M3{
16  bool MaCh3Program::write_file(const fs::path& path, std::string_view content) const{
17  try {
18  fs::create_directories(path.parent_path());
19  std::ofstream out(path);
20  if (!out) return false;
21  out << content;
22  return true;
23  } catch (...) {
24  return false;
25  }
26  }
27 
28  std::string MaCh3Program::detect_shell() const{
29  const char* shell = std::getenv("SHELL");
30  if (!shell) return "";
31 
32  std::string s(shell);
33  if (s.find("bash") != std::string::npos) return "bash";
34  if (s.find("zsh") != std::string::npos) return "zsh";
35 
36  return "";
37  }
38 
40  const char* home = std::getenv("HOME");
41  if (!home) {
42  std::cerr << "Cannot determine HOME directory\n";
43  return;
44  }
45 
46  std::string shell = detect_shell();
47  if (shell.empty()) {
48  std::cerr << "Could not detect your shell. Supported: bash, zsh\n";
49  return;
50  }
51 
52  fs::path home_path(home);
53 
54  if (shell == "bash") {
55  fs::path path = home_path / ".local/share/bash-completion/completions/mach3";
57  std::cout << "Installed bash completions → " << path << "\n";
58  return;
59  }
60 
61  if (shell == "zsh") {
62  fs::path path = home_path / ".local/share/zsh/site-functions/_mach3";
64  std::cout << "Installed zsh completions → " << path << "\n";
65  return;
66  }
67 
68  }
69 
70  void MaCh3Program::completions(const std::string& prefix) const{
71  for (const std::string& cmd : m_subcommands) {
72  if (cmd.rfind(prefix, 0) == 0)
73  std::cout << cmd << "\n";
74  }
75  }
76 
77  // void MaCh3Program::parse_args(int argc, const char *const argv[]) {
78  // try{
79  // MaCh3ArgumentParser::parse_args(argc, argv);
80 
81  // }
82  // catch (const std::exception& err) {
83  // if (auto prefix = this->present<std::string>("--complete")) {
84  // this->completions(*prefix);
85  // exit(0);
86  // }
87 
88  // if (this->get<bool>("--install-completions")) {
89  // this->install_completions();
90  // exit(0);
91  // }
92  // std::cerr << err.what() << std::endl;
93  // std::cerr << this->get_subcommand_used();
94  // MACH3LOG_ERROR(err.what());
95  // throw;
96  // }
97 
98  // if (!this->get_subcommand_used()){
99  // std::cerr << this->get_subcommand_used();
100  // throw NoArgsException();
101  // }
102  // }
103 
105  MaCh3ArgumentParser* parser = module.get_parser();
106  this->add_subparser(*parser);
107  m_module_map[parser] = &module;
108  m_subcommands.push_back(parser->name());
109  }
110 
117  const char* env = std::getenv("MACH3_PLUGINS");
118  if (!env) {
119  //std::cerr << "No plugins specified\n";
120  return;
121  }
122 
123  std::stringstream ss(env);
124  std::string path;
125 
126  while (std::getline(ss, path, ':')) {
127  for (const auto& sofile : this->expand_plugin_path(path)) {
128  try{
129  std::unique_ptr<DynamicPlugin> dlplugin = std::make_unique<DynamicPlugin>(sofile);
130  MaCh3ArgumentParser* parser = dlplugin->get_parser();
131  if (!parser) {
132  std::cerr << "Plugin " << sofile << " did not provide a valid parser.\n";
133  continue;
134  }
135 
136  this->add_subparser(*parser);
137  m_dynamic_plugin_map[parser] = std::move(dlplugin);
138  m_subcommands.push_back(parser->name());
139  }
140  catch (const std::exception& e) {
141  std::cerr << "Failed to load plugin from " << sofile << ": " << e.what() << "\n";
142  continue;
143  }
144  catch (...) {
145  std::cerr << "Failed to load plugin from " << sofile << ": unknown error\n";
146  continue;
147  }
148  }
149  }
150  }
151 
156  m_dynamic_plugin_map.clear();
157  }
158 
165 
166  const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used();
167 
168  if (sub_parser){
169  auto plugin_itr = m_module_map.find(&sub_parser);
170  if (plugin_itr != m_module_map.end()){
171  return plugin_itr->second->Run();
172  }
173  auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser);
174  if (dplugin_itr != m_dynamic_plugin_map.end()){
175  return dplugin_itr->second->Run();
176  }
177  }
178  return 0;
179  }
180 
181 
189  std::vector<std::string> MaCh3Program::expand_plugin_path(const std::string& path) const {
190  std::vector<std::string> result;
191 
192  fs::path p(path);
193 
194  if (!fs::exists(p)) {
195  std::cerr << "Plugin path does not exist: " << path << "\n";
196  return result;
197  }
198 
199  if (fs::is_regular_file(p)) {
200  // Single .so file
201  result.push_back(path);
202  }
203  else if (fs::is_directory(p)) {
204  // Load all .so files in directory
205  for (auto& entry : fs::directory_iterator(p)) {
206  if (entry.is_regular_file() && entry.path().extension() == ".so") {
207  result.push_back(entry.path().string());
208  }
209  }
210  }
211  else {
212  std::cerr << "Invalid plugin path (not file or directory): " << path << "\n";
213  }
214 
215  return result;
216  }
217 }
MaCh3 Logging utilities built on top of SPDLOG.
Core program class for the MaCh3 command-line interface.
Interface for MaCh3 plugins and modules.
Definition: plugin.hpp:16
virtual MaCh3ArgumentParser * get_parser()=0
Get the argument parser for this plugin.
Extended ArgumentParser with MaCh3-specific functionality.
Definition: m3argparse.hpp:17
const std::string name() const
Get the name of this parser/subcommand.
Definition: m3argparse.hpp:24
const MaCh3ArgumentParser & get_subcommand_used() const
Get the subcommand that was used in parsing.
Definition: m3argparse.hpp:40
void add_core_module(IModule &module)
Add a core module to the program.
void unload_dynamic_plugins()
Unload all dynamically loaded plugins.
bool write_file(const fs::path &path, std::string_view content) const
Write content to a file, creating parent directories if needed.
static constexpr std::string_view ZSH_COMPLETION
std::vector< std::string > m_subcommands
List of registered subcommand names.
void install_completions() const
Install shell completion scripts for bash and zsh.
static constexpr std::string_view BASH_COMPLETION
int Run()
Run the selected subcommand.
std::map< const MaCh3ArgumentParser *, IModule * > m_module_map
Map of parsers to core modules (non-owning)
void completions(const std::string &prefix) const
Generate completion suggestions for the given prefix.
std::map< const MaCh3ArgumentParser *, std::unique_ptr< DynamicPlugin > > m_dynamic_plugin_map
Map of parsers to dynamic plugins (owning)
std::string detect_shell() const
Detect the user's shell from the SHELL environment variable.
void load_dynamic_plugins()
Load dynamic plugins from the MACH3_PLUGINS environment variable.
std::vector< std::string > expand_plugin_path(const std::string &path) const
Expand a plugin path (file or directory) into a list of .so files.
Main namespace for MaCh3 software.