/** * * create_model.cc * An Tao * * Copyright 2018, An Tao. All rights reserved. * https://github.com/an-tao/drogon * Use of this source code is governed by a MIT license * that can be found in the License file. * * Drogon * */ #include "create_model.h" #include "cmd.h" #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #else #include #endif #include #include using namespace std::chrono_literals; using namespace drogon_ctl; static std::string toLower(const std::string &str) { auto ret = str; std::transform(ret.begin(), ret.end(), ret.begin(), tolower); return ret; } static std::map> getRelationships( const Json::Value &relationships) { std::map> ret; auto enabled = relationships.get("enabled", false).asBool(); if (!enabled) return ret; auto items = relationships["items"]; if (items.isNull()) return ret; if (!items.isArray()) { std::cerr << "items must be an array\n"; exit(1); } for (auto &relationship : items) { try { Relationship r(relationship); ret[r.originalTableName()].push_back(r); if (r.enableReverse() && (r.originalTableName() != r.targetTableName() || r.originalTableAlias() != r.targetTableAlias())) { auto reverse = r.reverse(); ret[reverse.originalTableName()].push_back(reverse); } } catch (const std::runtime_error &e) { std::cerr << e.what() << std::endl; exit(1); } } return ret; } #if USE_POSTGRESQL void create_model::createModelClassFromPG( const std::string &path, const DbClientPtr &client, const std::string &tableName, const std::string &schema, const Json::Value &restfulApiConfig, const std::vector &relationships) { auto className = nameTransform(tableName, true); HttpViewData data; data["className"] = className; data["tableName"] = toLower(tableName); data["hasPrimaryKey"] = (int)0; data["primaryKeyName"] = ""; data["dbName"] = dbname_; data["rdbms"] = std::string("postgresql"); data["relationships"] = relationships; if (schema != "public") { data["schema"] = schema; } std::vector cols; *client << "SELECT * \ FROM information_schema.columns \ WHERE table_schema = $1 \ AND table_name = $2" << schema << tableName << Mode::Blocking >> [&](const Result &r) { if (r.size() == 0) { std::cout << " ---Can't create model from the table " << tableName << ", please check privileges on the table." << std::endl; return; } for (Result::SizeType i = 0; i < r.size(); ++i) { auto row = r[i]; ColumnInfo info; info.index_ = i; info.dbType_ = "pg"; info.colName_ = row["column_name"].as(); info.colTypeName_ = nameTransform(info.colName_, true); info.colValName_ = nameTransform(info.colName_, false); auto isNullAble = row["is_nullable"].as(); info.notNull_ = isNullAble == "YES" ? false : true; auto type = row["data_type"].as(); info.colDatabaseType_ = type; if (type == "smallint") { info.colType_ = "short"; info.colLength_ = 2; } else if (type == "integer") { info.colType_ = "int32_t"; info.colLength_ = 4; } else if (type == "bigint" || type == "numeric") /// TODO:Use int64 to represent /// numeric type? { info.colType_ = "int64_t"; info.colLength_ = 8; } else if (type == "real") { info.colType_ = "float"; info.colLength_ = sizeof(float); } else if (type == "double precision") { info.colType_ = "double"; info.colLength_ = sizeof(double); } else if (type == "character varying") { info.colType_ = "std::string"; if (!row["character_maximum_length"].isNull()) info.colLength_ = row["character_maximum_length"].as(); } else if (type == "boolean") { info.colType_ = "bool"; info.colLength_ = 1; } else if (type == "date") { info.colType_ = "::trantor::Date"; } else if (type.find("timestamp") != std::string::npos) { info.colType_ = "::trantor::Date"; } else if (type == "bytea") { info.colType_ = "std::vector"; } else { info.colType_ = "std::string"; } auto defaultVal = row["column_default"].as(); if (!defaultVal.empty()) { info.hasDefaultVal_ = true; if (defaultVal.find("nextval(") == 0) { info.isAutoVal_ = true; } } auto isIdentity = row["is_identity"].as(); if (isIdentity == "YES") { info.isAutoVal_ = true; } cols.push_back(std::move(info)); } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; size_t pkNumber = 0; *client << "SELECT \ pg_constraint.conname AS pk_name,\ pg_constraint.conkey AS pk_vector \ FROM pg_constraint \ INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \ WHERE \ pg_class.relname = $1 \ AND pg_constraint.contype = 'p'" << tableName << Mode::Blocking >> [&](bool isNull, const std::string &pkName, const std::vector> &pk) { if (!isNull) { pkNumber = pk.size(); } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; data["hasPrimaryKey"] = (int)pkNumber; if (pkNumber == 1) { *client << "SELECT \ pg_attribute.attname AS colname,\ pg_type.typname AS typename,\ pg_constraint.contype AS contype \ FROM pg_constraint \ INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \ INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \ AND pg_attribute.attnum = pg_constraint.conkey [ 1 ] \ INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \ WHERE pg_class.relname = $1 and pg_constraint.contype='p'" << tableName << Mode::Blocking >> [&](bool isNull, const std::string &colName, const std::string &type) { if (isNull) return; data["primaryKeyName"] = colName; for (auto &col : cols) { if (col.colName_ == colName) { col.isPrimaryKey_ = true; data["primaryKeyType"] = col.colType_; } } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; } else if (pkNumber > 1) { std::vector pkNames, pkTypes; for (size_t i = 1; i <= pkNumber; ++i) { *client << "SELECT \ pg_attribute.attname AS colname,\ pg_type.typname AS typename,\ pg_constraint.contype AS contype \ FROM pg_constraint \ INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \ INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \ AND pg_attribute.attnum = pg_constraint.conkey [ $1 ] \ INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \ WHERE pg_class.relname = $2 and pg_constraint.contype='p'" << (int)i << tableName << Mode::Blocking >> [&](bool isNull, std::string colName, const std::string &type) { if (isNull) return; pkNames.push_back(colName); for (auto &col : cols) { if (col.colName_ == colName) { col.isPrimaryKey_ = true; pkTypes.push_back(col.colType_); } } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; } data["primaryKeyName"] = pkNames; data["primaryKeyType"] = pkTypes; } data["columns"] = cols; std::ofstream headerFile(path + "/" + className + ".h", std::ofstream::out); std::ofstream sourceFile(path + "/" + className + ".cc", std::ofstream::out); auto templ = DrTemplateBase::newTemplate("model_h.csp"); headerFile << templ->genText(data); templ = DrTemplateBase::newTemplate("model_cc.csp"); sourceFile << templ->genText(data); createRestfulAPIController(data, restfulApiConfig); } void create_model::createModelFromPG( const std::string &path, const DbClientPtr &client, const std::string &schema, const Json::Value &restfulApiConfig, std::map> &relationships) { *client << "SELECT a.oid," "a.relname AS name," "b.description AS comment " "FROM pg_class a " "LEFT OUTER JOIN pg_description b ON b.objsubid = 0 AND a.oid = " "b.objoid " "WHERE a.relnamespace = (SELECT oid FROM pg_namespace WHERE " "nspname = $1) " "AND a.relkind = 'r' ORDER BY a.relname" << schema << Mode::Blocking >> [&](bool isNull, size_t oid, std::string &&tableName, const std::string &comment) { if (!isNull) { std::cout << "table name:" << tableName << std::endl; createModelClassFromPG(path, client, tableName, schema, restfulApiConfig, relationships[tableName]); } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; } #endif #if USE_MYSQL void create_model::createModelClassFromMysql( const std::string &path, const DbClientPtr &client, const std::string &tableName, const Json::Value &restfulApiConfig, const std::vector &relationships) { auto className = nameTransform(tableName, true); HttpViewData data; data["className"] = className; data["tableName"] = toLower(tableName); data["hasPrimaryKey"] = (int)0; data["primaryKeyName"] = ""; data["dbName"] = dbname_; data["rdbms"] = std::string("mysql"); data["relationships"] = relationships; std::vector cols; int i = 0; *client << "desc " + tableName << Mode::Blocking >> [&i, &cols](bool isNull, const std::string &field, const std::string &type, const std::string &isNullAble, const std::string &key, const std::string &defaultVal, const std::string &extra) { if (!isNull) { ColumnInfo info; info.index_ = i; info.dbType_ = "mysql"; info.colName_ = field; info.colTypeName_ = nameTransform(info.colName_, true); info.colValName_ = nameTransform(info.colName_, false); info.notNull_ = isNullAble == "YES" ? false : true; info.colDatabaseType_ = type; info.isPrimaryKey_ = key == "PRI" ? true : false; if (type.find("tinyint") == 0) { info.colType_ = "int8_t"; info.colLength_ = 1; } else if (type.find("smallint") == 0) { info.colType_ = "int16_t"; info.colLength_ = 2; } else if (type.find("int") == 0) { info.colType_ = "int32_t"; info.colLength_ = 4; } else if (type.find("bigint") == 0) { info.colType_ = "int64_t"; info.colLength_ = 8; } else if (type.find("float") == 0) { info.colType_ = "float"; info.colLength_ = sizeof(float); } else if (type.find("double") == 0) { info.colType_ = "double"; info.colLength_ = sizeof(double); } else if (type.find("date") == 0 || type.find("datetime") == 0 || type.find("timestamp") == 0) { info.colType_ = "::trantor::Date"; } else if (type.find("blob") != std::string::npos) { info.colType_ = "std::vector"; } else if (type.find("varchar") != std::string::npos) { info.colType_ = "std::string"; auto pos1 = type.find("("); auto pos2 = type.find(")"); if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 - pos1 > 1) { info.colLength_ = std::stoll(type.substr(pos1 + 1, pos2 - pos1 - 1)); } } else { info.colType_ = "std::string"; } if (type.find("unsigned") != std::string::npos) { info.colType_ = "u" + info.colType_; } if (!defaultVal.empty()) { info.hasDefaultVal_ = true; } if (extra.find("auto_") == 0) { info.isAutoVal_ = true; } cols.push_back(std::move(info)); ++i; } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; std::vector pkNames, pkTypes; for (auto const &col : cols) { if (col.isPrimaryKey_) { pkNames.push_back(col.colName_); pkTypes.push_back(col.colType_); } } data["hasPrimaryKey"] = (int)pkNames.size(); if (pkNames.size() == 1) { data["primaryKeyName"] = pkNames[0]; data["primaryKeyType"] = pkTypes[0]; } else if (pkNames.size() > 1) { data["primaryKeyName"] = pkNames; data["primaryKeyType"] = pkTypes; } data["columns"] = cols; std::ofstream headerFile(path + "/" + className + ".h", std::ofstream::out); std::ofstream sourceFile(path + "/" + className + ".cc", std::ofstream::out); auto templ = DrTemplateBase::newTemplate("model_h.csp"); headerFile << templ->genText(data); templ = DrTemplateBase::newTemplate("model_cc.csp"); sourceFile << templ->genText(data); createRestfulAPIController(data, restfulApiConfig); } void create_model::createModelFromMysql( const std::string &path, const DbClientPtr &client, const Json::Value &restfulApiConfig, std::map> &relationships) { *client << "show tables" << Mode::Blocking >> [&](bool isNull, std::string &&tableName) { if (!isNull) { std::cout << "table name:" << tableName << std::endl; createModelClassFromMysql(path, client, tableName, restfulApiConfig, relationships[tableName]); } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; } #endif #if USE_SQLITE3 void create_model::createModelClassFromSqlite3( const std::string &path, const DbClientPtr &client, const std::string &tableName, const Json::Value &restfulApiConfig, const std::vector &relationships) { HttpViewData data; auto className = nameTransform(tableName, true); data["className"] = className; data["tableName"] = toLower(tableName); data["hasPrimaryKey"] = (int)0; data["primaryKeyName"] = ""; data["dbName"] = std::string("sqlite3"); data["rdbms"] = std::string("sqlite3"); data["relationships"] = relationships; std::vector cols; std::string sql = "PRAGMA table_info(" + tableName + ");"; *client << sql << Mode::Blocking >> [&](const Result &result) { size_t index = 0; for (auto &row : result) { bool notnull = row["notnull"].as(); bool primary = row["pk"].as(); auto type = row["type"].as(); std::transform(type.begin(), type.end(), type.begin(), tolower); ColumnInfo info; info.index_ = index++; info.dbType_ = "sqlite3"; info.colName_ = row["name"].as(); info.colTypeName_ = nameTransform(info.colName_, true); info.colValName_ = nameTransform(info.colName_, false); info.notNull_ = notnull; info.colDatabaseType_ = type; info.isPrimaryKey_ = primary; if (primary) { *client << "SELECT sql FROM sqlite_master WHERE name=? and " "(type='table' or type='view');" << tableName << Mode::Blocking >> [&](bool isNull, std::string sql) { if (!isNull) { std::transform(sql.begin(), sql.end(), sql.begin(), tolower); if (sql.find("autoincrement") != std::string::npos) { info.isAutoVal_ = true; } } } >> [](const DrogonDbException &e) { }; } auto defaultVal = row["dflt_value"].as(); if (!defaultVal.empty()) { info.hasDefaultVal_ = true; } if (type.find("int") != std::string::npos) { info.colType_ = "uint64_t"; info.colLength_ = 8; } else if (type.find("char") != std::string::npos || type == "text" || type == "clob") { info.colType_ = "std::string"; } else if (type.find("double") != std::string::npos || type == "real" || type == "float") { info.colType_ = "double"; info.colLength_ = sizeof(double); } else if (type == "bool") { info.colType_ = "bool"; info.colLength_ = 1; } else if (type == "blob") { info.colType_ = "std::vector"; } else { info.colType_ = "std::string"; } cols.push_back(std::move(info)); } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; std::vector pkNames, pkTypes; for (auto const &col : cols) { if (col.isPrimaryKey_) { pkNames.push_back(col.colName_); pkTypes.push_back(col.colType_); } data["hasPrimaryKey"] = (int)pkNames.size(); if (pkNames.size() == 1) { data["primaryKeyName"] = pkNames[0]; data["primaryKeyType"] = pkTypes[0]; } else if (pkNames.size() > 1) { data["primaryKeyName"] = pkNames; data["primaryKeyType"] = pkTypes; } data["columns"] = cols; std::ofstream headerFile(path + "/" + className + ".h", std::ofstream::out); std::ofstream sourceFile(path + "/" + className + ".cc", std::ofstream::out); auto templ = DrTemplateBase::newTemplate("model_h.csp"); headerFile << templ->genText(data); templ = DrTemplateBase::newTemplate("model_cc.csp"); sourceFile << templ->genText(data); createRestfulAPIController(data, restfulApiConfig); } } void create_model::createModelFromSqlite3( const std::string &path, const DbClientPtr &client, const Json::Value &restfulApiConfig, std::map> &relationships) { *client << "SELECT name FROM sqlite_master WHERE name!='sqlite_sequence' " "and (type='table' or type='view') ORDER BY name;" << Mode::Blocking >> [&](bool isNull, std::string &&tableName) mutable { if (!isNull) { std::cout << "table name:" << tableName << std::endl; createModelClassFromSqlite3(path, client, tableName, restfulApiConfig, relationships[tableName]); } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; exit(1); }; } #endif void create_model::createModel(const std::string &path, const Json::Value &config, const std::string &singleModelName) { auto dbType = config.get("rdbms", "no dbms").asString(); std::transform(dbType.begin(), dbType.end(), dbType.begin(), tolower); auto restfulApiConfig = config["restful_api_controllers"]; auto relationships = getRelationships(config["relationships"]); if (dbType == "postgresql") { #if USE_POSTGRESQL std::cout << "postgresql" << std::endl; auto host = config.get("host", "127.0.0.1").asString(); auto port = config.get("port", 5432).asUInt(); auto dbname = config.get("dbname", "").asString(); if (dbname == "") { std::cerr << "Please configure dbname in " << path << "/model.json " << std::endl; exit(1); } dbname_ = dbname; auto user = config.get("user", "").asString(); if (user == "") { std::cerr << "Please configure user in " << path << "/model.json " << std::endl; exit(1); } auto password = config.get("passwd", "").asString(); if (password.empty()) { password = config.get("password", "").asString(); } auto connStr = utils::formattedString("host=%s port=%u dbname=%s user=%s", host.c_str(), port, dbname.c_str(), user.c_str()); if (!password.empty()) { connStr += " password="; connStr += password; } auto characterSet = config.get("client_encoding", "").asString(); if (!characterSet.empty()) { connStr += " client_encoding="; connStr += characterSet; } auto schema = config.get("schema", "public").asString(); DbClientPtr client = drogon::orm::DbClient::newPgClient(connStr, 1); std::cout << "Connect to server..." << std::endl; if (forceOverwrite_) { std::this_thread::sleep_for(2s); } else { std::cout << "Source files in the " << path << " folder will be overwritten, continue(y/n)?\n"; auto in = getchar(); (void)getchar(); // get the return key if (in != 'Y' && in != 'y') { std::cout << "Abort!" << std::endl; exit(0); } } if (singleModelName.empty()) { auto tables = config["tables"]; if (!tables || tables.size() == 0) createModelFromPG( path, client, schema, restfulApiConfig, relationships); else { for (int i = 0; i < (int)tables.size(); ++i) { auto tableName = tables[i].asString(); std::transform(tableName.begin(), tableName.end(), tableName.begin(), tolower); std::cout << "table name:" << tableName << std::endl; createModelClassFromPG(path, client, tableName, schema, restfulApiConfig, relationships[tableName]); } } } else { createModelClassFromPG(path, client, singleModelName, schema, restfulApiConfig, relationships[singleModelName]); } #else std::cerr << "Drogon does not support PostgreSQL, please install PostgreSQL " "development environment before installing drogon" << std::endl; #endif } else if (dbType == "mysql") { #if USE_MYSQL std::cout << "mysql" << std::endl; auto host = config.get("host", "127.0.0.1").asString(); auto port = config.get("port", 5432).asUInt(); auto dbname = config.get("dbname", "").asString(); if (dbname == "") { std::cerr << "Please configure dbname in " << path << "/model.json " << std::endl; exit(1); } dbname_ = dbname; auto user = config.get("user", "").asString(); if (user == "") { std::cerr << "Please configure user in " << path << "/model.json " << std::endl; exit(1); } auto password = config.get("passwd", "").asString(); if (password.empty()) { password = config.get("password", "").asString(); } auto connStr = utils::formattedString("host=%s port=%u dbname=%s user=%s", host.c_str(), port, dbname.c_str(), user.c_str()); if (!password.empty()) { connStr += " password="; connStr += password; } auto characterSet = config.get("client_encoding", "").asString(); if (!characterSet.empty()) { connStr += " client_encoding="; connStr += characterSet; } DbClientPtr client = drogon::orm::DbClient::newMysqlClient(connStr, 1); std::cout << "Connect to server..." << std::endl; if (forceOverwrite_) { std::this_thread::sleep_for(2s); } else { std::cout << "Source files in the " << path << " folder will be overwritten, continue(y/n)?\n"; auto in = getchar(); (void)getchar(); // get the return key if (in != 'Y' && in != 'y') { std::cout << "Abort!" << std::endl; exit(0); } } if (singleModelName.empty()) { auto tables = config["tables"]; if (!tables || tables.size() == 0) createModelFromMysql(path, client, restfulApiConfig, relationships); else { for (int i = 0; i < (int)tables.size(); ++i) { auto tableName = tables[i].asString(); std::transform(tableName.begin(), tableName.end(), tableName.begin(), tolower); std::cout << "table name:" << tableName << std::endl; createModelClassFromMysql(path, client, tableName, restfulApiConfig, relationships[tableName]); } } } else { createModelClassFromMysql(path, client, singleModelName, restfulApiConfig, relationships[singleModelName]); } #else std::cerr << "Drogon does not support Mysql, please install MariaDB " "development environment before installing drogon" << std::endl; #endif } else if (dbType == "sqlite3") { #if USE_SQLITE3 auto filename = config.get("filename", "").asString(); if (filename == "") { std::cerr << "Please configure filename in " << path << "/model.json " << std::endl; exit(1); } std::string connStr = "filename=" + filename; DbClientPtr client = drogon::orm::DbClient::newSqlite3Client(connStr, 1); std::cout << "Connect..." << std::endl; if (forceOverwrite_) { std::this_thread::sleep_for(1s); } else { std::cout << "Source files in the " << path << " folder will be overwritten, continue(y/n)?\n"; auto in = getchar(); (void)getchar(); // get the return key if (in != 'Y' && in != 'y') { std::cout << "Abort!" << std::endl; exit(0); } } if (singleModelName.empty()) { auto tables = config["tables"]; if (!tables || tables.size() == 0) createModelFromSqlite3(path, client, restfulApiConfig, relationships); else { for (int i = 0; i < (int)tables.size(); ++i) { auto tableName = tables[i].asString(); std::transform(tableName.begin(), tableName.end(), tableName.begin(), tolower); std::cout << "table name:" << tableName << std::endl; createModelClassFromSqlite3(path, client, tableName, restfulApiConfig, relationships[tableName]); } } } else { createModelClassFromSqlite3(path, client, singleModelName, restfulApiConfig, relationships[singleModelName]); } #else std::cerr << "Drogon does not support Sqlite3, please install Sqlite3 " "development environment before installing drogon" << std::endl; #endif } else if (dbType == "no dbms") { std::cerr << "Please configure Model in " << path << "/model.json " << std::endl; exit(1); } else { std::cerr << "Does not support " << dbType << std::endl; exit(1); } } void create_model::createModel(const std::string &path, const std::string &singleModelName) { #ifndef _WIN32 DIR *dp; if ((dp = opendir(path.c_str())) == NULL) { std::cerr << "No such file or directory : " << path << std::endl; return; } closedir(dp); #endif auto configFile = path + "/model.json"; #ifdef _WIN32 if (_access(configFile.c_str(), 0) != 0) #else if (access(configFile.c_str(), 0) != 0) #endif { std::cerr << "Config file " << configFile << " not found!" << std::endl; exit(1); } #ifdef _WIN32 if (_access(configFile.c_str(), 04) != 0) #else if (access(configFile.c_str(), R_OK) != 0) #endif { std::cerr << "No permission to read config file " << configFile << std::endl; exit(1); } std::ifstream infile(configFile.c_str(), std::ifstream::in); if (infile) { Json::Value configJsonRoot; try { infile >> configJsonRoot; createModel(path, configJsonRoot, singleModelName); } catch (const std::exception &exception) { std::cerr << "Configuration file format error! in " << configFile << ":" << std::endl; std::cerr << exception.what() << std::endl; exit(1); } } } void create_model::handleCommand(std::vector ¶meters) { std::cout << "Create model" << std::endl; if (parameters.size() == 0) { std::cerr << "Missing Model path name!" << std::endl; } std::string singleModelName; for (auto iter = parameters.begin(); iter != parameters.end(); ++iter) { if ((*iter).find("--table=") == 0) { singleModelName = (*iter).substr(8); parameters.erase(iter); break; } } for (auto iter = parameters.begin(); iter != parameters.end(); ++iter) { if ((*iter) == "-f") { forceOverwrite_ = true; parameters.erase(iter); break; } } for (auto const &path : parameters) { createModel(path, singleModelName); } } void create_model::createRestfulAPIController( const DrTemplateData &tableInfo, const Json::Value &restfulApiConfig) { if (restfulApiConfig.isNull()) return; if (!restfulApiConfig.get("enabled", false).asBool()) { return; } auto genBaseOnly = restfulApiConfig.get("generate_base_only", false).asBool(); auto modelClassName = tableInfo.get("className"); std::regex regex("\\*"); auto resource = std::regex_replace( restfulApiConfig.get("resource_uri", "/*").asString(), regex, modelClassName); std::transform(resource.begin(), resource.end(), resource.begin(), tolower); auto ctrlClassName = std::regex_replace(restfulApiConfig.get("class_name", "/*").asString(), regex, modelClassName); std::regex regex1("::"); std::string ctlName = std::regex_replace(ctrlClassName, regex1, std::string("_")); auto v = utils::splitString(ctrlClassName, "::"); drogon::DrTemplateData data; data.insert("className", v[v.size() - 1]); v.pop_back(); data.insert("namespaceVector", v); data.insert("resource", resource); data.insert("fileName", ctlName); data.insert("tableName", tableInfo.get("tableName")); data.insert("tableInfo", tableInfo); auto filters = restfulApiConfig["filters"]; if (filters.isNull() || filters.empty() || !filters.isArray()) { data.insert("filters", ""); } else { std::string filtersStr; for (auto &filterName : filters) { filtersStr += ",\""; filtersStr.append(filterName.asString()); filtersStr += '"'; } data.insert("filters", filtersStr); } auto dbClientConfig = restfulApiConfig["db_client"]; if (dbClientConfig.isNull() || dbClientConfig.empty()) { data.insertAsString("dbClientName", "default"); data.insert("isFastDbClient", false); } else { auto clientName = dbClientConfig.get("name", "default").asString(); auto isFast = dbClientConfig.get("is_fast", false).asBool(); data.insertAsString("dbClientName", clientName); data.insert("isFastDbClient", isFast); } auto dir = restfulApiConfig.get("directory", "controllers").asString(); if (dir[dir.length() - 1] != '/') { dir += '/'; } { std::string headFileName = dir + ctlName + "Base.h"; std::string sourceFilename = dir + ctlName + "Base.cc"; // { // std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in); // std::ifstream iSourceFile(sourceFilename.c_str(), // std::ifstream::in); // if (iHeadFile || iSourceFile) // { // std::cout << "The " << headFileName << " and " << // sourceFilename // << " you want to create already exist, " // "overwrite it(y/n)?" // << std::endl; // auto in = getchar(); // (void)getchar(); // get the return key // if (in != 'Y' && in != 'y') // { // std::cout << "Abort!" << std::endl; // exit(0); // } // } // } std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out); std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out); if (!oHeadFile || !oSourceFile) { perror(""); exit(1); } try { auto templ = DrTemplateBase::newTemplate("restful_controller_base_h.csp"); oHeadFile << templ->genText(data); templ = DrTemplateBase::newTemplate("restful_controller_base_cc.csp"); oSourceFile << templ->genText(data); } catch (const std::exception &err) { std::cerr << err.what() << std::endl; exit(1); } std::cout << "create a http restful API controller base class:" << ctrlClassName << "Base" << std::endl; std::cout << "file name: " << headFileName << ", " << sourceFilename << std::endl << std::endl; } if (!genBaseOnly) { std::string headFileName = dir + ctlName + ".h"; std::string sourceFilename = dir + ctlName + ".cc"; if (!forceOverwrite_) { std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in); std::ifstream iSourceFile(sourceFilename.c_str(), std::ifstream::in); if (iHeadFile || iSourceFile) { std::cout << "The " << headFileName << " and " << sourceFilename << " you want to create already exist, " "overwrite them(y/n)?" << std::endl; auto in = getchar(); (void)getchar(); // get the return key if (in != 'Y' && in != 'y') { std::cout << "Abort!" << std::endl; exit(0); } } } std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out); std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out); if (!oHeadFile || !oSourceFile) { perror(""); exit(1); } try { auto templ = DrTemplateBase::newTemplate("restful_controller_custom_h.csp"); oHeadFile << templ->genText(data); templ = DrTemplateBase::newTemplate("restful_controller_custom_cc.csp"); oSourceFile << templ->genText(data); } catch (const std::exception &err) { std::cerr << err.what() << std::endl; exit(1); } std::cout << "create a http restful API controller class: " << ctrlClassName << std::endl; std::cout << "file name: " << headFileName << ", " << sourceFilename << std::endl << std::endl; } }