548 lines
18 KiB
C++
548 lines
18 KiB
C++
/**
|
|
*
|
|
* @file create_view.cc
|
|
* @author 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_view.h"
|
|
#include "cmd.h"
|
|
#include <drogon/utils/Utilities.h>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <regex>
|
|
|
|
static const std::string cxx_include = "<%inc";
|
|
static const std::string cxx_end = "%>";
|
|
static const std::string cxx_lang = "<%c++";
|
|
static const std::string cxx_view_data = "@@";
|
|
static const std::string cxx_output = "$$";
|
|
static const std::string cxx_val_start = "[[";
|
|
static const std::string cxx_val_end = "]]";
|
|
static const std::string sub_view_start = "<%view";
|
|
static const std::string sub_view_end = "%>";
|
|
|
|
using namespace drogon_ctl;
|
|
|
|
static std::string &replace_all(std::string &str,
|
|
const std::string &old_value,
|
|
const std::string &new_value)
|
|
{
|
|
std::string::size_type pos(0);
|
|
while (true)
|
|
{
|
|
// std::cout<<str<<endl;
|
|
// std::cout<<"pos="<<pos<<endl;
|
|
if ((pos = str.find(old_value, pos)) != std::string::npos)
|
|
{
|
|
str = str.replace(pos, old_value.length(), new_value);
|
|
pos += new_value.length() - old_value.length();
|
|
++pos;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return str;
|
|
}
|
|
static void parseCxxLine(std::ofstream &oSrcFile,
|
|
const std::string &line,
|
|
const std::string &streamName,
|
|
const std::string &viewDataName)
|
|
{
|
|
if (line.length() > 0)
|
|
{
|
|
std::string tmp = line;
|
|
replace_all(tmp, cxx_output, streamName);
|
|
replace_all(tmp, cxx_view_data, viewDataName);
|
|
oSrcFile << tmp << "\n";
|
|
}
|
|
}
|
|
static void outputVal(std::ofstream &oSrcFile,
|
|
const std::string &streamName,
|
|
const std::string &viewDataName,
|
|
const std::string &keyName)
|
|
{
|
|
oSrcFile << "{\n";
|
|
oSrcFile << " auto & val=" << viewDataName << "[\"" << keyName
|
|
<< "\"];\n";
|
|
oSrcFile << " if(val.type()==typeid(const char *)){\n";
|
|
oSrcFile << " " << streamName
|
|
<< "<<*any_cast<const char *>(&val);\n";
|
|
oSrcFile << " }else "
|
|
"if(val.type()==typeid(std::string)||val.type()==typeid(const "
|
|
"std::string)){\n";
|
|
oSrcFile << " " << streamName
|
|
<< "<<*any_cast<const std::string>(&val);\n";
|
|
oSrcFile << " }\n";
|
|
oSrcFile << "}\n";
|
|
}
|
|
|
|
static void outputSubView(std::ofstream &oSrcFile,
|
|
const std::string &streamName,
|
|
const std::string &viewDataName,
|
|
const std::string &keyName)
|
|
{
|
|
oSrcFile << "{\n";
|
|
oSrcFile << " auto templ=DrTemplateBase::newTemplate(\"" << keyName
|
|
<< "\");\n";
|
|
oSrcFile << " if(templ){\n";
|
|
oSrcFile << " " << streamName << "<< templ->genText(" << viewDataName
|
|
<< ");\n";
|
|
oSrcFile << " }\n";
|
|
oSrcFile << "}\n";
|
|
}
|
|
|
|
static void parseLine(std::ofstream &oSrcFile,
|
|
std::string &line,
|
|
const std::string &streamName,
|
|
const std::string &viewDataName,
|
|
int &cxx_flag,
|
|
int returnFlag = 1)
|
|
{
|
|
std::string::size_type pos(0);
|
|
// std::cout<<line<<"("<<line.length()<<")\n";
|
|
if (line.length() > 0 && line[line.length() - 1] == '\r')
|
|
{
|
|
line.resize(line.length() - 1);
|
|
}
|
|
if (line.length() == 0)
|
|
{
|
|
// std::cout<<"blank line!"<<std::endl;
|
|
// std::cout<<streamName<<"<<\"\\n\";\n";
|
|
if (returnFlag)
|
|
oSrcFile << streamName << "<<\"\\n\";\n";
|
|
return;
|
|
}
|
|
if (cxx_flag == 0)
|
|
{
|
|
// find cxx lang begin
|
|
if ((pos = line.find(cxx_lang)) != std::string::npos)
|
|
{
|
|
std::string oldLine = line.substr(0, pos);
|
|
if (oldLine.length() > 0)
|
|
parseLine(
|
|
oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);
|
|
std::string newLine = line.substr(pos + cxx_lang.length());
|
|
cxx_flag = 1;
|
|
if (newLine.length() > 0)
|
|
parseLine(oSrcFile,
|
|
newLine,
|
|
streamName,
|
|
viewDataName,
|
|
cxx_flag,
|
|
returnFlag);
|
|
}
|
|
else
|
|
{
|
|
if ((pos = line.find(cxx_val_start)) != std::string::npos)
|
|
{
|
|
std::string oldLine = line.substr(0, pos);
|
|
parseLine(
|
|
oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);
|
|
std::string newLine = line.substr(pos + cxx_val_start.length());
|
|
if ((pos = newLine.find(cxx_val_end)) != std::string::npos)
|
|
{
|
|
std::string keyName = newLine.substr(0, pos);
|
|
auto iter = keyName.begin();
|
|
while (iter != keyName.end() && *iter == ' ')
|
|
++iter;
|
|
auto iterEnd = iter;
|
|
while (iterEnd != keyName.end() && *iterEnd != ' ')
|
|
++iterEnd;
|
|
keyName = std::string(iter, iterEnd);
|
|
outputVal(oSrcFile, streamName, viewDataName, keyName);
|
|
std::string tailLine =
|
|
newLine.substr(pos + cxx_val_end.length());
|
|
parseLine(oSrcFile,
|
|
tailLine,
|
|
streamName,
|
|
viewDataName,
|
|
cxx_flag,
|
|
returnFlag);
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "format err!" << std::endl;
|
|
exit(1);
|
|
}
|
|
}
|
|
else if ((pos = line.find(sub_view_start)) != std::string::npos)
|
|
{
|
|
std::string oldLine = line.substr(0, pos);
|
|
parseLine(
|
|
oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);
|
|
std::string newLine =
|
|
line.substr(pos + sub_view_start.length());
|
|
if ((pos = newLine.find(sub_view_end)) != std::string::npos)
|
|
{
|
|
std::string keyName = newLine.substr(0, pos);
|
|
auto iter = keyName.begin();
|
|
while (iter != keyName.end() && *iter == ' ')
|
|
++iter;
|
|
auto iterEnd = iter;
|
|
while (iterEnd != keyName.end() && *iterEnd != ' ')
|
|
++iterEnd;
|
|
keyName = std::string(iter, iterEnd);
|
|
outputSubView(oSrcFile, streamName, viewDataName, keyName);
|
|
std::string tailLine =
|
|
newLine.substr(pos + sub_view_end.length());
|
|
parseLine(oSrcFile,
|
|
tailLine,
|
|
streamName,
|
|
viewDataName,
|
|
cxx_flag,
|
|
returnFlag);
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "format err!" << std::endl;
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (line.length() > 0)
|
|
{
|
|
replace_all(line, "\\", "\\\\");
|
|
replace_all(line, "\"", "\\\"");
|
|
oSrcFile << "\t" << streamName << " << \"" << line;
|
|
}
|
|
if (returnFlag)
|
|
oSrcFile << "\\n\";\n";
|
|
else
|
|
oSrcFile << "\";\n";
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pos = line.find(cxx_end)) != std::string::npos)
|
|
{
|
|
std::string newLine = line.substr(0, pos);
|
|
parseCxxLine(oSrcFile, newLine, streamName, viewDataName);
|
|
std::string oldLine = line.substr(pos + cxx_end.length());
|
|
cxx_flag = 0;
|
|
if (oldLine.length() > 0)
|
|
parseLine(oSrcFile,
|
|
oldLine,
|
|
streamName,
|
|
viewDataName,
|
|
cxx_flag,
|
|
returnFlag);
|
|
}
|
|
else
|
|
{
|
|
parseCxxLine(oSrcFile, line, streamName, viewDataName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void create_view::handleCommand(std::vector<std::string> ¶meters)
|
|
{
|
|
for (auto iter = parameters.begin(); iter != parameters.end();)
|
|
{
|
|
auto &file = *iter;
|
|
if (file == "-o" || file == "--output")
|
|
{
|
|
iter = parameters.erase(iter);
|
|
if (iter != parameters.end())
|
|
{
|
|
outputPath_ = *iter;
|
|
iter = parameters.erase(iter);
|
|
}
|
|
continue;
|
|
}
|
|
else if (file == "-n" || file == "--namespace")
|
|
{
|
|
iter = parameters.erase(iter);
|
|
if (iter != parameters.end())
|
|
{
|
|
namespaces_ = utils::splitString(*iter, "::");
|
|
iter = parameters.erase(iter);
|
|
}
|
|
continue;
|
|
}
|
|
else if (file == "--path-to-namespace")
|
|
{
|
|
iter = parameters.erase(iter);
|
|
pathToNamespaceFlag_ = true;
|
|
continue;
|
|
}
|
|
else if (file[0] == '-')
|
|
{
|
|
std::cout << ARGS_ERROR_STR << std::endl;
|
|
return;
|
|
}
|
|
++iter;
|
|
}
|
|
createViewFiles(parameters);
|
|
}
|
|
void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
|
|
{
|
|
for (auto const &file : cspFileNames)
|
|
{
|
|
std::cout << "create view:" << file << std::endl;
|
|
createViewFile(file);
|
|
}
|
|
}
|
|
int create_view::createViewFile(const std::string &script_filename)
|
|
{
|
|
std::cout << "create HttpView Class file by " << script_filename
|
|
<< std::endl;
|
|
if (pathToNamespaceFlag_)
|
|
{
|
|
std::string::size_type pos1 = 0, pos2 = 0;
|
|
if (script_filename.length() >= 2 && script_filename[0] == '.' &&
|
|
(script_filename[1] == '/' || script_filename[1] == '\\'))
|
|
{
|
|
pos1 = pos2 = 2;
|
|
}
|
|
else if (script_filename.length() >= 1 &&
|
|
(script_filename[0] == '/' || script_filename[0] == '\\'))
|
|
{
|
|
pos1 = pos2 = 1;
|
|
}
|
|
while (pos2 < script_filename.length() - 1)
|
|
{
|
|
if (script_filename[pos2] == '/' || script_filename[pos2] == '\\')
|
|
{
|
|
if (pos2 > pos1)
|
|
{
|
|
namespaces_.push_back(
|
|
script_filename.substr(pos1, pos2 - pos1));
|
|
}
|
|
pos1 = ++pos2;
|
|
}
|
|
else
|
|
{
|
|
++pos2;
|
|
}
|
|
}
|
|
}
|
|
std::string npPrefix;
|
|
for (auto &np : namespaces_)
|
|
{
|
|
npPrefix += np;
|
|
npPrefix += "_";
|
|
}
|
|
std::ifstream infile(script_filename.c_str(), std::ifstream::in);
|
|
if (infile)
|
|
{
|
|
std::string::size_type pos = script_filename.rfind('.');
|
|
if (pos != std::string::npos)
|
|
{
|
|
std::string className = script_filename.substr(0, pos);
|
|
if ((pos = className.rfind('/')) != std::string::npos)
|
|
{
|
|
className = className.substr(pos + 1);
|
|
}
|
|
std::cout << "className=" << className << std::endl;
|
|
std::string headFileName =
|
|
outputPath_ + "/" + npPrefix + className + ".h";
|
|
std::string sourceFilename =
|
|
outputPath_ + "/" + npPrefix + className + ".cc";
|
|
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
|
std::ofstream oSourceFile(sourceFilename.c_str(),
|
|
std::ofstream::out);
|
|
if (!oHeadFile || !oSourceFile)
|
|
{
|
|
std::cerr << "Can't open " << headFileName << " or "
|
|
<< sourceFilename << "\n";
|
|
return -1;
|
|
}
|
|
|
|
newViewHeaderFile(oHeadFile, className);
|
|
newViewSourceFile(oSourceFile, className, npPrefix, infile);
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "can't open file " << script_filename << std::endl;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
void create_view::newViewHeaderFile(std::ofstream &file,
|
|
const std::string &className)
|
|
{
|
|
file << "//this file is generated by program automatically,don't modify "
|
|
"it!\n";
|
|
file << "#include <drogon/DrTemplate.h>\n";
|
|
for (auto &np : namespaces_)
|
|
{
|
|
file << "namespace " << np << "\n";
|
|
file << "{\n";
|
|
}
|
|
file << "class " << className << ":public drogon::DrTemplate<" << className
|
|
<< ">\n";
|
|
file << "{\npublic:\n\t" << className << "(){};\n\tvirtual ~" << className
|
|
<< "(){};\n\t"
|
|
"virtual std::string genText(const drogon::DrTemplateData &) "
|
|
"override;\n};\n";
|
|
for (auto i = 0; i < namespaces_.size(); ++i)
|
|
{
|
|
file << "}\n";
|
|
}
|
|
}
|
|
|
|
void create_view::newViewSourceFile(std::ofstream &file,
|
|
const std::string &className,
|
|
const std::string &namespacePrefix,
|
|
std::ifstream &infile)
|
|
{
|
|
file << "//this file is generated by program(drogon_ctl) "
|
|
"automatically,don't modify it!\n";
|
|
file << "#include \"" << namespacePrefix << className << ".h\"\n";
|
|
file << "#include <drogon/utils/OStringStream.h>\n";
|
|
file << "#include <string>\n";
|
|
file << "#include <map>\n";
|
|
file << "#include <vector>\n";
|
|
file << "#include <set>\n";
|
|
file << "#include <iostream>\n";
|
|
file << "#include <unordered_map>\n";
|
|
file << "#include <unordered_set>\n";
|
|
file << "#include <algorithm>\n";
|
|
file << "#include <list>\n";
|
|
file << "#include <deque>\n";
|
|
file << "#include <queue>\n";
|
|
|
|
// Find layout tag
|
|
std::string layoutName;
|
|
std::regex layoutReg("<%layout[ \\t]+(((?!%\\}).)*[^ \\t])[ \\t]*%>");
|
|
for (std::string buffer; std::getline(infile, buffer);)
|
|
{
|
|
std::smatch results;
|
|
if (std::regex_search(buffer, results, layoutReg))
|
|
{
|
|
if (results.size() > 1)
|
|
{
|
|
layoutName = results[1].str();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
infile.clear();
|
|
infile.seekg(0, std::ifstream::beg);
|
|
bool import_flag{false};
|
|
for (std::string buffer; std::getline(infile, buffer);)
|
|
{
|
|
std::string::size_type pos(0);
|
|
|
|
if (!import_flag)
|
|
{
|
|
std::string lowerBuffer = buffer;
|
|
std::transform(lowerBuffer.begin(),
|
|
lowerBuffer.end(),
|
|
lowerBuffer.begin(),
|
|
::tolower);
|
|
if ((pos = lowerBuffer.find(cxx_include)) != std::string::npos)
|
|
{
|
|
// std::cout<<"haha find it!"<<endl;
|
|
std::string newLine = buffer.substr(pos + cxx_include.length());
|
|
import_flag = true;
|
|
if ((pos = newLine.find(cxx_end)) != std::string::npos)
|
|
{
|
|
newLine = newLine.substr(0, pos);
|
|
file << newLine << "\n";
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
file << newLine << "\n";
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// std::cout<<buffer<<endl;
|
|
if ((pos = buffer.find(cxx_end)) != std::string::npos)
|
|
{
|
|
std::string newLine = buffer.substr(0, pos);
|
|
file << newLine << "\n";
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// std::cout<<"to source file"<<buffer<<endl;
|
|
file << buffer << "\n";
|
|
}
|
|
}
|
|
}
|
|
// std::cout<<"import_flag="<<import_flag<<std::endl;
|
|
if (!import_flag)
|
|
{
|
|
infile.clear();
|
|
infile.seekg(0, std::ifstream::beg);
|
|
}
|
|
|
|
if (!namespaces_.empty())
|
|
{
|
|
file << "using namespace ";
|
|
for (int i = 0; i < namespaces_.size(); ++i)
|
|
{
|
|
if (i != namespaces_.size() - 1)
|
|
{
|
|
file << namespaces_[i] << "::";
|
|
}
|
|
else
|
|
{
|
|
file << namespaces_[i] << ";";
|
|
}
|
|
}
|
|
file << "\n";
|
|
}
|
|
file << "using namespace drogon;\n";
|
|
std::string viewDataName = className + "_view_data";
|
|
// virtual std::string genText(const DrTemplateData &)
|
|
file << "std::string " << className << "::genText(const DrTemplateData& "
|
|
<< viewDataName << ")\n{\n";
|
|
// std::string bodyName=className+"_bodystr";
|
|
std::string streamName = className + "_tmp_stream";
|
|
|
|
// oSrcFile <<"\tstd::string "<<bodyName<<";\n";
|
|
file << "\tdrogon::OStringStream " << streamName << ";\n";
|
|
file << "\tstd::string layoutName{\"" << layoutName << "\"};\n";
|
|
int cxx_flag = 0;
|
|
for (std::string buffer; std::getline(infile, buffer);)
|
|
{
|
|
if (buffer.length() > 0)
|
|
{
|
|
std::smatch results;
|
|
if (std::regex_search(buffer, results, layoutReg))
|
|
{
|
|
if (results.size() > 1)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
std::regex re("\\{%[ \\t]*(((?!%\\}).)*[^ \\t])[ \\t]*%\\}");
|
|
buffer = std::regex_replace(buffer, re, "<%c++$$$$<<$1;%>");
|
|
}
|
|
parseLine(file, buffer, streamName, viewDataName, cxx_flag);
|
|
}
|
|
file << "if(layoutName.empty())\n{\n";
|
|
file << "std::string ret{std::move(" << streamName << ".str())};\n";
|
|
file << "return ret;\n}else\n{\n";
|
|
file << "auto templ = DrTemplateBase::newTemplate(layoutName);\n";
|
|
file << "if(!templ) return \"\";\n";
|
|
file << "HttpViewData data = " << viewDataName << ";\n";
|
|
file << "auto str = std::move(" << streamName << ".str());\n";
|
|
file << "if(!str.empty() && str[str.length()-1] == '\\n') "
|
|
"str.resize(str.length()-1);\n";
|
|
file << "data[\"\"] = std::move(str);\n";
|
|
file << "return templ->genText(data);\n";
|
|
file << "}\n}\n";
|
|
}
|