mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-05-07 03:59:35 +00:00
code: production,dbal,hpp (3 files)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Command-line argument parser
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
namespace tools {
|
||||
|
||||
class ArgParser {
|
||||
public:
|
||||
ArgParser(int argc, char* argv[]) {
|
||||
parse(argc, argv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get positional argument by index (0-based, excludes flags)
|
||||
*/
|
||||
std::string get_positional(size_t index) const {
|
||||
return index < positional_.size() ? positional_[index] : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get option value by name (e.g., --name value)
|
||||
*/
|
||||
std::string get_option(const std::string& name, const std::string& default_value = "") const {
|
||||
auto it = options_.find(name);
|
||||
return it != options_.end() ? it->second : default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get integer option
|
||||
*/
|
||||
int get_int_option(const std::string& name, int default_value = 0) const {
|
||||
auto value = get_option(name);
|
||||
if (value.empty()) return default_value;
|
||||
try {
|
||||
return std::stoi(value);
|
||||
} catch (...) {
|
||||
return default_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if flag is present (e.g., --dry-run)
|
||||
*/
|
||||
bool has_flag(const std::string& name) const {
|
||||
return flags_.count(name) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comma-separated list option
|
||||
*/
|
||||
std::vector<std::string> get_list_option(const std::string& name) const {
|
||||
auto value = get_option(name);
|
||||
if (value.empty()) return {};
|
||||
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss(value);
|
||||
std::string item;
|
||||
|
||||
while (std::getline(ss, item, ',')) {
|
||||
// Trim whitespace
|
||||
item.erase(0, item.find_first_not_of(" \t"));
|
||||
item.erase(item.find_last_not_of(" \t") + 1);
|
||||
if (!item.empty()) {
|
||||
result.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all positional arguments
|
||||
*/
|
||||
const std::vector<std::string>& positional_args() const {
|
||||
return positional_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> positional_;
|
||||
std::unordered_map<std::string, std::string> options_;
|
||||
std::unordered_set<std::string> flags_;
|
||||
|
||||
// Options that take values
|
||||
const std::unordered_set<std::string> value_options_ = {
|
||||
"name", "description", "category", "min-level",
|
||||
"entities", "components", "deps", "output"
|
||||
};
|
||||
|
||||
void parse(int argc, char* argv[]) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg.substr(0, 2) == "--") {
|
||||
auto name = arg.substr(2);
|
||||
|
||||
// Check if this option takes a value
|
||||
if (value_options_.count(name) && i + 1 < argc) {
|
||||
options_[name] = argv[++i];
|
||||
} else {
|
||||
flags_.insert(name);
|
||||
}
|
||||
} else if (arg[0] == '-') {
|
||||
// Short flag (e.g., -h, -v)
|
||||
flags_.insert(arg.substr(1));
|
||||
} else {
|
||||
positional_.push_back(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Lua Runner - sol2-based Lua script executor
|
||||
*
|
||||
* Provides a sandboxed Lua environment for running package template scripts.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#include <sol/sol.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace tools {
|
||||
|
||||
/**
|
||||
* Configuration object to pass to Lua functions
|
||||
*/
|
||||
class LuaConfig {
|
||||
public:
|
||||
json data;
|
||||
|
||||
void set(const std::string& key, const std::string& value) {
|
||||
if (!value.empty()) {
|
||||
data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void set(const std::string& key, int value) {
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
void set(const std::string& key, bool value) {
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
void set_list(const std::string& key, const std::vector<std::string>& values) {
|
||||
if (!values.empty()) {
|
||||
data[key] = values;
|
||||
} else {
|
||||
data[key] = json::array();
|
||||
}
|
||||
}
|
||||
|
||||
static LuaConfig from_json(const std::string& json_str) {
|
||||
LuaConfig config;
|
||||
config.data = json::parse(json_str);
|
||||
return config;
|
||||
}
|
||||
|
||||
sol::table to_lua_table(sol::state& lua) const {
|
||||
return json_to_lua(lua, data);
|
||||
}
|
||||
|
||||
private:
|
||||
sol::object json_to_lua(sol::state& lua, const json& j) const {
|
||||
if (j.is_null()) {
|
||||
return sol::nil;
|
||||
} else if (j.is_boolean()) {
|
||||
return sol::make_object(lua, j.get<bool>());
|
||||
} else if (j.is_number_integer()) {
|
||||
return sol::make_object(lua, j.get<int64_t>());
|
||||
} else if (j.is_number_float()) {
|
||||
return sol::make_object(lua, j.get<double>());
|
||||
} else if (j.is_string()) {
|
||||
return sol::make_object(lua, j.get<std::string>());
|
||||
} else if (j.is_array()) {
|
||||
sol::table arr = lua.create_table();
|
||||
int idx = 1;
|
||||
for (const auto& elem : j) {
|
||||
arr[idx++] = json_to_lua(lua, elem);
|
||||
}
|
||||
return arr;
|
||||
} else if (j.is_object()) {
|
||||
sol::table obj = lua.create_table();
|
||||
for (auto& [key, val] : j.items()) {
|
||||
obj[key] = json_to_lua(lua, val);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return sol::nil;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generated file structure
|
||||
*/
|
||||
struct GeneratedFile {
|
||||
std::string path;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
using GeneratedFiles = std::vector<GeneratedFile>;
|
||||
|
||||
/**
|
||||
* Validation result from Lua
|
||||
*/
|
||||
struct ValidationResult {
|
||||
bool valid = false;
|
||||
std::vector<std::string> errors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lua script runner with sandboxed environment
|
||||
*/
|
||||
class LuaRunner {
|
||||
public:
|
||||
explicit LuaRunner(const fs::path& scripts_path)
|
||||
: scripts_path_(scripts_path) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a Lua module by name
|
||||
*/
|
||||
bool load_module(const std::string& module_name) {
|
||||
auto module_path = scripts_path_ / module_name / "init.lua";
|
||||
if (!fs::exists(module_path)) {
|
||||
// Try direct file
|
||||
module_path = scripts_path_ / (module_name + ".lua");
|
||||
}
|
||||
|
||||
if (!fs::exists(module_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
auto result = lua_.safe_script_file(module_path.string());
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
last_error_ = err.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the module
|
||||
loaded_modules_[module_name] = result.get<sol::table>();
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
last_error_ = e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a Lua function with config, returning typed result
|
||||
*/
|
||||
template<typename T>
|
||||
T call(const std::string& func_path, const LuaConfig& config) {
|
||||
return call_impl<T>(func_path, config.to_lua_table(lua_));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a Lua function without arguments
|
||||
*/
|
||||
template<typename T>
|
||||
T call(const std::string& func_path) {
|
||||
return call_impl<T>(func_path, sol::nil);
|
||||
}
|
||||
|
||||
const std::string& last_error() const { return last_error_; }
|
||||
|
||||
private:
|
||||
sol::state lua_;
|
||||
fs::path scripts_path_;
|
||||
std::string last_error_;
|
||||
std::unordered_map<std::string, sol::table> loaded_modules_;
|
||||
|
||||
void initialize() {
|
||||
// Open safe libraries only (no os, io, debug)
|
||||
lua_.open_libraries(
|
||||
sol::lib::base,
|
||||
sol::lib::string,
|
||||
sol::lib::table,
|
||||
sol::lib::math,
|
||||
sol::lib::utf8
|
||||
);
|
||||
|
||||
// Set up package path
|
||||
std::string package_path = lua_["package"]["path"];
|
||||
package_path += ";" + scripts_path_.string() + "/?.lua";
|
||||
package_path += ";" + scripts_path_.string() + "/?/init.lua";
|
||||
lua_["package"]["path"] = package_path;
|
||||
|
||||
// Add custom require that respects sandbox
|
||||
setup_sandboxed_require();
|
||||
}
|
||||
|
||||
void setup_sandboxed_require() {
|
||||
// Override require to prevent loading unsafe modules
|
||||
lua_.set_function("safe_require", [this](const std::string& name) -> sol::object {
|
||||
// Block dangerous modules
|
||||
static const std::vector<std::string> blocked = {
|
||||
"os", "io", "debug", "ffi", "package.loadlib"
|
||||
};
|
||||
|
||||
for (const auto& b : blocked) {
|
||||
if (name == b || name.find(b + ".") == 0) {
|
||||
throw std::runtime_error("Module '" + name + "' is not allowed in sandbox");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if already loaded
|
||||
if (loaded_modules_.count(name)) {
|
||||
return loaded_modules_[name];
|
||||
}
|
||||
|
||||
// Try to load
|
||||
if (load_module(name)) {
|
||||
return loaded_modules_[name];
|
||||
}
|
||||
|
||||
throw std::runtime_error("Module '" + name + "' not found");
|
||||
});
|
||||
|
||||
// Replace require
|
||||
lua_.script(R"(
|
||||
local original_require = require
|
||||
require = function(name)
|
||||
local ok, result = pcall(safe_require, name)
|
||||
if ok then return result end
|
||||
return original_require(name)
|
||||
end
|
||||
)");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T call_impl(const std::string& func_path, sol::object arg) {
|
||||
// Parse "module.function" path
|
||||
auto dot_pos = func_path.find('.');
|
||||
if (dot_pos == std::string::npos) {
|
||||
throw std::runtime_error("Invalid function path: " + func_path);
|
||||
}
|
||||
|
||||
auto module_name = func_path.substr(0, dot_pos);
|
||||
auto func_name = func_path.substr(dot_pos + 1);
|
||||
|
||||
if (!loaded_modules_.count(module_name)) {
|
||||
throw std::runtime_error("Module not loaded: " + module_name);
|
||||
}
|
||||
|
||||
auto& module = loaded_modules_[module_name];
|
||||
sol::function func = module[func_name];
|
||||
|
||||
if (!func.valid()) {
|
||||
throw std::runtime_error("Function not found: " + func_path);
|
||||
}
|
||||
|
||||
sol::protected_function_result result;
|
||||
if (arg == sol::nil) {
|
||||
result = func();
|
||||
} else {
|
||||
result = func(arg);
|
||||
}
|
||||
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
throw std::runtime_error(err.what());
|
||||
}
|
||||
|
||||
return convert_result<T>(result);
|
||||
}
|
||||
|
||||
// Specializations for different return types
|
||||
template<typename T>
|
||||
T convert_result(sol::protected_function_result& result);
|
||||
};
|
||||
|
||||
// Specialization for GeneratedFiles
|
||||
template<>
|
||||
inline GeneratedFiles LuaRunner::convert_result<GeneratedFiles>(sol::protected_function_result& result) {
|
||||
GeneratedFiles files;
|
||||
sol::table lua_files = result;
|
||||
|
||||
for (auto& pair : lua_files) {
|
||||
sol::table file_table = pair.second;
|
||||
GeneratedFile file;
|
||||
file.path = file_table["path"].get<std::string>();
|
||||
file.content = file_table["content"].get<std::string>();
|
||||
files.push_back(std::move(file));
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
// Specialization for ValidationResult
|
||||
template<>
|
||||
inline ValidationResult LuaRunner::convert_result<ValidationResult>(sol::protected_function_result& result) {
|
||||
ValidationResult validation;
|
||||
sol::table lua_result = result;
|
||||
|
||||
validation.valid = lua_result["valid"].get<bool>();
|
||||
|
||||
if (lua_result["errors"].valid()) {
|
||||
sol::table errors = lua_result["errors"];
|
||||
for (auto& pair : errors) {
|
||||
validation.errors.push_back(pair.second.as<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
return validation;
|
||||
}
|
||||
|
||||
// Specialization for vector<string>
|
||||
template<>
|
||||
inline std::vector<std::string> LuaRunner::convert_result<std::vector<std::string>>(sol::protected_function_result& result) {
|
||||
std::vector<std::string> vec;
|
||||
sol::table lua_table = result;
|
||||
|
||||
for (auto& pair : lua_table) {
|
||||
vec.push_back(pair.second.as<std::string>());
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* Package Template Generator CLI
|
||||
*
|
||||
* C++ CLI that executes Lua scripts to generate MetaBuilder package scaffolding.
|
||||
*
|
||||
* Usage:
|
||||
* package_generator new <package_id> [options]
|
||||
* package_generator quick <package_id> [options]
|
||||
* package_generator list-categories
|
||||
* package_generator validate <config.json>
|
||||
*
|
||||
* Examples:
|
||||
* package_generator new my_package --category tools --min-level 3
|
||||
* package_generator quick my_widget --dependency
|
||||
* package_generator new my_forum --with-schema --entities Thread,Post
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "lua_runner.hpp"
|
||||
#include "arg_parser.hpp"
|
||||
#include "file_writer.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
constexpr const char* VERSION = "1.0.0";
|
||||
constexpr const char* PROGRAM_NAME = "package_generator";
|
||||
|
||||
void print_help() {
|
||||
std::cout << R"(
|
||||
Package Template Generator v)" << VERSION << R"(
|
||||
|
||||
USAGE:
|
||||
)" << PROGRAM_NAME << R"( <command> [options]
|
||||
|
||||
COMMANDS:
|
||||
new <package_id> Create a new package with full scaffolding
|
||||
quick <package_id> Create a minimal package quickly
|
||||
list-categories List available package categories
|
||||
validate <file> Validate a package config JSON file
|
||||
help Show this help message
|
||||
|
||||
OPTIONS:
|
||||
--name <name> Display name (default: derived from package_id)
|
||||
--description <desc> Package description
|
||||
--category <cat> Package category (default: ui)
|
||||
--min-level <n> Minimum access level 0-6 (default: 2)
|
||||
--primary Package can own routes (default)
|
||||
--dependency Package is dependency-only
|
||||
--with-schema Include database schema scaffolding
|
||||
--entities <e1,e2> Entity names for schema (comma-separated)
|
||||
--with-components Include component scaffolding
|
||||
--components <c1,c2> Component names (comma-separated)
|
||||
--deps <d1,d2> Package dependencies (comma-separated)
|
||||
--output <dir> Output directory (default: ./packages)
|
||||
--dry-run Preview files without writing
|
||||
|
||||
CATEGORIES:
|
||||
ui, editors, tools, social, media, gaming,
|
||||
admin, config, core, demo, development, managers
|
||||
|
||||
EXAMPLES:
|
||||
)" << PROGRAM_NAME << R"( new my_package
|
||||
)" << PROGRAM_NAME << R"( new my_forum --with-schema --entities Thread,Post,Reply
|
||||
)" << PROGRAM_NAME << R"( quick my_widget --dependency --category ui
|
||||
)" << PROGRAM_NAME << R"( new dashboard --with-components --components StatCard,Chart
|
||||
|
||||
)";
|
||||
}
|
||||
|
||||
void print_version() {
|
||||
std::cout << PROGRAM_NAME << " version " << VERSION << std::endl;
|
||||
}
|
||||
|
||||
int cmd_new(const tools::ArgParser& args, tools::LuaRunner& lua) {
|
||||
auto package_id = args.get_positional(1);
|
||||
if (package_id.empty()) {
|
||||
std::cerr << "Error: package_id is required\n";
|
||||
std::cerr << "Usage: " << PROGRAM_NAME << " new <package_id> [options]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Build config table for Lua
|
||||
tools::LuaConfig config;
|
||||
config.set("packageId", package_id);
|
||||
config.set("name", args.get_option("name", ""));
|
||||
config.set("description", args.get_option("description", ""));
|
||||
config.set("category", args.get_option("category", "ui"));
|
||||
config.set("minLevel", args.get_int_option("min-level", 2));
|
||||
config.set("primary", !args.has_flag("dependency"));
|
||||
config.set("withSchema", args.has_flag("with-schema"));
|
||||
config.set("withTests", true);
|
||||
config.set("withComponents", args.has_flag("with-components"));
|
||||
|
||||
// Parse comma-separated lists
|
||||
config.set_list("entities", args.get_list_option("entities"));
|
||||
config.set_list("components", args.get_list_option("components"));
|
||||
config.set_list("dependencies", args.get_list_option("deps"));
|
||||
|
||||
// Validate config via Lua
|
||||
auto validation = lua.call<tools::ValidationResult>("package_template.validate_config", config);
|
||||
if (!validation.valid) {
|
||||
std::cerr << "Validation failed:\n";
|
||||
for (const auto& err : validation.errors) {
|
||||
std::cerr << " - " << err << "\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Generate files
|
||||
auto files = lua.call<tools::GeneratedFiles>("package_template.generate", config);
|
||||
|
||||
if (args.has_flag("dry-run")) {
|
||||
std::cout << "Would generate " << files.size() << " files:\n";
|
||||
for (const auto& file : files) {
|
||||
std::cout << " " << file.path << " (" << file.content.size() << " bytes)\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write files
|
||||
auto output_dir = args.get_option("output", "packages");
|
||||
auto package_path = fs::path(output_dir) / package_id;
|
||||
|
||||
if (fs::exists(package_path)) {
|
||||
std::cerr << "Error: Package directory already exists: " << package_path << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
tools::FileWriter writer(package_path);
|
||||
int written = writer.write_all(files);
|
||||
|
||||
std::cout << "\n✅ Package '" << package_id << "' created successfully!\n";
|
||||
std::cout << " Location: " << package_path << "\n";
|
||||
std::cout << " Files: " << written << "\n";
|
||||
std::cout << "\nNext steps:\n";
|
||||
std::cout << " 1. Review generated files in " << package_path << "\n";
|
||||
std::cout << " 2. Add package-specific logic to seed/scripts/\n";
|
||||
std::cout << " 3. Run tests: npm run test:package " << package_id << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_quick(const tools::ArgParser& args, tools::LuaRunner& lua) {
|
||||
auto package_id = args.get_positional(1);
|
||||
if (package_id.empty()) {
|
||||
std::cerr << "Error: package_id is required\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Minimal config
|
||||
tools::LuaConfig config;
|
||||
config.set("packageId", package_id);
|
||||
config.set("category", args.get_option("category", "ui"));
|
||||
config.set("minLevel", args.get_int_option("min-level", 2));
|
||||
config.set("primary", !args.has_flag("dependency"));
|
||||
config.set("withSchema", false);
|
||||
config.set("withTests", false);
|
||||
config.set("withComponents", false);
|
||||
|
||||
auto files = lua.call<tools::GeneratedFiles>("package_template.generate_quick", config);
|
||||
|
||||
if (args.has_flag("dry-run")) {
|
||||
std::cout << "Would generate " << files.size() << " files:\n";
|
||||
for (const auto& file : files) {
|
||||
std::cout << " " << file.path << "\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto output_dir = args.get_option("output", "packages");
|
||||
auto package_path = fs::path(output_dir) / package_id;
|
||||
|
||||
if (fs::exists(package_path)) {
|
||||
std::cerr << "Error: Package directory already exists: " << package_path << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
tools::FileWriter writer(package_path);
|
||||
writer.write_all(files);
|
||||
|
||||
std::cout << "✅ Package '" << package_id << "' created (quick mode)\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_list_categories(tools::LuaRunner& lua) {
|
||||
auto categories = lua.call<std::vector<std::string>>("package_template.get_categories");
|
||||
|
||||
std::cout << "Available package categories:\n\n";
|
||||
for (const auto& cat : categories) {
|
||||
std::cout << " - " << cat << "\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_validate(const tools::ArgParser& args, tools::LuaRunner& lua) {
|
||||
auto config_file = args.get_positional(1);
|
||||
if (config_file.empty()) {
|
||||
std::cerr << "Error: config file path is required\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!fs::exists(config_file)) {
|
||||
std::cerr << "Error: File not found: " << config_file << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read JSON config
|
||||
std::ifstream file(config_file);
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
|
||||
auto config = tools::LuaConfig::from_json(buffer.str());
|
||||
auto validation = lua.call<tools::ValidationResult>("package_template.validate_config", config);
|
||||
|
||||
if (validation.valid) {
|
||||
std::cout << "✅ Configuration is valid\n";
|
||||
return 0;
|
||||
} else {
|
||||
std::cout << "❌ Configuration has errors:\n";
|
||||
for (const auto& err : validation.errors) {
|
||||
std::cout << " - " << err << "\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
tools::ArgParser args(argc, argv);
|
||||
|
||||
if (args.has_flag("help") || args.has_flag("h") || argc < 2) {
|
||||
print_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (args.has_flag("version") || args.has_flag("v")) {
|
||||
print_version();
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto command = args.get_positional(0);
|
||||
|
||||
// Initialize Lua runner with package scripts path
|
||||
auto scripts_path = fs::current_path() / "packages" / "codegen_studio" / "seed" / "scripts";
|
||||
if (!fs::exists(scripts_path)) {
|
||||
// Try relative to executable
|
||||
auto exe_path = fs::path(argv[0]).parent_path();
|
||||
scripts_path = exe_path / ".." / ".." / "packages" / "codegen_studio" / "seed" / "scripts";
|
||||
}
|
||||
|
||||
tools::LuaRunner lua(scripts_path);
|
||||
|
||||
if (!lua.load_module("package_template")) {
|
||||
std::cerr << "Error: Failed to load package_template module\n";
|
||||
std::cerr << "Looked in: " << scripts_path << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (command == "new") {
|
||||
return cmd_new(args, lua);
|
||||
} else if (command == "quick") {
|
||||
return cmd_quick(args, lua);
|
||||
} else if (command == "list-categories") {
|
||||
return cmd_list_categories(lua);
|
||||
} else if (command == "validate") {
|
||||
return cmd_validate(args, lua);
|
||||
} else if (command == "help") {
|
||||
print_help();
|
||||
return 0;
|
||||
} else {
|
||||
std::cerr << "Unknown command: " << command << "\n";
|
||||
std::cerr << "Run '" << PROGRAM_NAME << " help' for usage\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user