decided in favor of dedicated COMPONENT() macro, updates to documentation
This commit is contained in:
+120
-23
@@ -146,6 +146,18 @@ String preprocess_named_render_syntax(String content)
|
||||
line = indent + "EXPORT void render_" + safe_name(render_name) + render_signature;
|
||||
}
|
||||
}
|
||||
else if(trimmed.rfind("COMPONENT:", 0) == 0)
|
||||
{
|
||||
String signature = trimmed.substr(10);
|
||||
auto open_paren_pos = signature.find("(");
|
||||
if(open_paren_pos != String::npos)
|
||||
{
|
||||
String render_name = trim(signature.substr(0, open_paren_pos));
|
||||
String render_signature = signature.substr(open_paren_pos);
|
||||
if(render_name != "")
|
||||
line = indent + "EXPORT void component_render_" + safe_name(render_name) + render_signature;
|
||||
}
|
||||
}
|
||||
|
||||
result += line + line_break;
|
||||
current_line = "";
|
||||
@@ -285,6 +297,7 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
//setup_unit_paths(context, su, file_name);
|
||||
|
||||
su->on_render = 0;
|
||||
su->on_component = 0;
|
||||
su->on_websocket = 0;
|
||||
su->on_setup = 0;
|
||||
su->compiler_messages = "";
|
||||
@@ -308,6 +321,8 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
printf("Error - %s in %s\n", error, su->file_name.c_str());
|
||||
su->on_render = (request_ref_handler)dlsym(su->so_handle, "render");
|
||||
dlerror();
|
||||
su->on_component = (request_ref_handler)dlsym(su->so_handle, "component_render");
|
||||
dlerror();
|
||||
su->on_websocket = (request_ref_handler)dlsym(su->so_handle, "websocket");
|
||||
dlerror();
|
||||
su->api_declarations = split(file_get_contents(su->api_file_name), "\n");
|
||||
@@ -466,6 +481,34 @@ SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct UnitInvocationScope
|
||||
{
|
||||
Request* context = 0;
|
||||
String previous_working_directory;
|
||||
String previous_unit_file;
|
||||
|
||||
UnitInvocationScope(Request* context, SharedUnit* su)
|
||||
{
|
||||
this->context = context;
|
||||
previous_working_directory = get_cwd();
|
||||
previous_unit_file = context->resources.current_unit_file;
|
||||
set_cwd(su->src_path);
|
||||
context->resources.current_unit_file = su->file_name;
|
||||
}
|
||||
|
||||
~UnitInvocationScope()
|
||||
{
|
||||
if(!context)
|
||||
return;
|
||||
context->resources.current_unit_file = previous_unit_file;
|
||||
set_cwd(previous_working_directory);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
String component_normalize_path(String name)
|
||||
{
|
||||
name = trim(name);
|
||||
@@ -477,26 +520,30 @@ String component_normalize_path(String name)
|
||||
void component_parse_target(String target, String& file_name, String& render_name)
|
||||
{
|
||||
target = trim(target);
|
||||
render_name = "render";
|
||||
render_name = "";
|
||||
auto render_split_pos = target.find(":");
|
||||
if(render_split_pos != String::npos)
|
||||
{
|
||||
render_name = trim(target.substr(render_split_pos + 1));
|
||||
target = trim(target.substr(0, render_split_pos));
|
||||
if(render_name == "")
|
||||
render_name = "render";
|
||||
}
|
||||
file_name = target;
|
||||
}
|
||||
|
||||
String component_resolve_path(String name)
|
||||
String component_resolve_path(String name, Request* request_context = 0)
|
||||
{
|
||||
String target_name = trim(name);
|
||||
String file_name;
|
||||
String render_name;
|
||||
component_parse_target(name, file_name, render_name);
|
||||
component_parse_target(target_name, file_name, render_name);
|
||||
|
||||
if(file_name == "")
|
||||
return("");
|
||||
{
|
||||
if(target_name.rfind(":", 0) == 0 && request_context && request_context->resources.current_unit_file != "")
|
||||
file_name = request_context->resources.current_unit_file;
|
||||
else
|
||||
return("");
|
||||
}
|
||||
|
||||
StringList candidates;
|
||||
auto push_candidate = [&] (String candidate) {
|
||||
@@ -530,7 +577,7 @@ String component_resolve_path(String name)
|
||||
return("");
|
||||
}
|
||||
|
||||
String render_handler_symbol(String render_name)
|
||||
String page_render_handler_symbol(String render_name)
|
||||
{
|
||||
render_name = trim(render_name);
|
||||
if(render_name == "" || render_name == "render")
|
||||
@@ -538,9 +585,17 @@ String render_handler_symbol(String render_name)
|
||||
return("render_" + safe_name(render_name));
|
||||
}
|
||||
|
||||
request_ref_handler get_render_handler(SharedUnit* su, String render_name)
|
||||
String component_handler_symbol(String render_name)
|
||||
{
|
||||
String symbol = render_handler_symbol(render_name);
|
||||
render_name = trim(render_name);
|
||||
if(render_name == "")
|
||||
return("component_render");
|
||||
return("component_render_" + safe_name(render_name));
|
||||
}
|
||||
|
||||
request_ref_handler get_page_render_handler(SharedUnit* su, String render_name)
|
||||
{
|
||||
String symbol = page_render_handler_symbol(render_name);
|
||||
if(symbol == "render")
|
||||
return(su->on_render);
|
||||
|
||||
@@ -554,6 +609,22 @@ request_ref_handler get_render_handler(SharedUnit* su, String render_name)
|
||||
return(handler);
|
||||
}
|
||||
|
||||
request_ref_handler get_component_handler(SharedUnit* su, String render_name)
|
||||
{
|
||||
String symbol = component_handler_symbol(render_name);
|
||||
if(symbol == "component_render")
|
||||
return(su->on_component);
|
||||
|
||||
auto it = su->api_functions.find(symbol);
|
||||
if(it != su->api_functions.end())
|
||||
return((request_ref_handler)it->second);
|
||||
|
||||
auto handler = (request_ref_handler)dlsym(su->so_handle, symbol.c_str());
|
||||
dlerror();
|
||||
su->api_functions[symbol] = (void*)handler;
|
||||
return(handler);
|
||||
}
|
||||
|
||||
bool compiler_invoke_render(Request* context, String file_name, String render_name, String* error_out = 0)
|
||||
{
|
||||
auto su = compiler_load_shared_unit(context, file_name, "", false);
|
||||
@@ -567,7 +638,7 @@ bool compiler_invoke_render(Request* context, String file_name, String render_na
|
||||
return(false);
|
||||
}
|
||||
|
||||
auto handler = get_render_handler(su, render_name);
|
||||
auto handler = get_page_render_handler(su, render_name);
|
||||
if(!handler)
|
||||
{
|
||||
if(error_out)
|
||||
@@ -580,11 +651,41 @@ bool compiler_invoke_render(Request* context, String file_name, String render_na
|
||||
return(false);
|
||||
}
|
||||
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
UnitInvocationScope invoke_scope(context, su);
|
||||
su->on_setup(context);
|
||||
handler(*context);
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool compiler_invoke_component(Request* context, String file_name, String render_name, String* error_out = 0)
|
||||
{
|
||||
auto su = compiler_load_shared_unit(context, file_name, "", false);
|
||||
if(!su)
|
||||
return(false);
|
||||
|
||||
if(!su->on_setup)
|
||||
{
|
||||
if(error_out)
|
||||
*error_out = "internal error: set_current_request() not defined in " + file_name;
|
||||
return(false);
|
||||
}
|
||||
|
||||
auto handler = get_component_handler(su, render_name);
|
||||
if(!handler)
|
||||
{
|
||||
if(error_out)
|
||||
{
|
||||
if(trim(render_name) == "")
|
||||
*error_out = "no COMPONENT() entry point";
|
||||
else
|
||||
*error_out = "no COMPONENT:" + render_name + "() entry point";
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
UnitInvocationScope invoke_scope(context, su);
|
||||
su->on_setup(context);
|
||||
handler(*context);
|
||||
set_cwd(prev_wd);
|
||||
return(true);
|
||||
}
|
||||
|
||||
@@ -618,11 +719,9 @@ void compiler_invoke_websocket(Request* context, String file_name)
|
||||
return;
|
||||
}
|
||||
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
UnitInvocationScope invoke_scope(context, su);
|
||||
su->on_setup(context);
|
||||
su->on_websocket(*context);
|
||||
set_cwd(prev_wd);
|
||||
}
|
||||
|
||||
void render_file(String file_name)
|
||||
@@ -638,7 +737,7 @@ void render_file(String file_name, Request& context)
|
||||
|
||||
String component_resolve(String name)
|
||||
{
|
||||
return(component_resolve_path(name));
|
||||
return(component_resolve_path(name, context));
|
||||
}
|
||||
|
||||
bool component_exists(String name)
|
||||
@@ -674,10 +773,10 @@ void render_component(String name, DTree props, Request& context)
|
||||
String render_name;
|
||||
component_parse_target(name, file_name, render_name);
|
||||
|
||||
String resolved_name = component_resolve_path(file_name);
|
||||
String resolved_name = component_resolve_path(name, &context);
|
||||
if(resolved_name == "")
|
||||
{
|
||||
print(component_error_banner("component not found: " + file_name));
|
||||
print(component_error_banner("component not found: " + trim(name)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -685,7 +784,7 @@ void render_component(String name, DTree props, Request& context)
|
||||
context.call = props;
|
||||
|
||||
String error_message = "";
|
||||
if(!compiler_invoke_render(&context, resolved_name, render_name, &error_message) && error_message != "")
|
||||
if(!compiler_invoke_component(&context, resolved_name, render_name, &error_message) && error_message != "")
|
||||
print(component_error_banner(error_message));
|
||||
|
||||
context.call = previous_call;
|
||||
@@ -747,11 +846,9 @@ DTree* call_file(String file_name, String function_name, DTree* call_param)
|
||||
}
|
||||
else
|
||||
{
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
UnitInvocationScope invoke_scope(context, su);
|
||||
su->on_setup(context);
|
||||
result = f(call_param);
|
||||
set_cwd(prev_wd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define RENDER(X) extern "C" void render(Request& context)
|
||||
#define COMPONENT(X) extern "C" void component_render(Request& context)
|
||||
#define WS(X) extern "C" void websocket(Request& context)
|
||||
#define EXPORT extern "C"
|
||||
|
||||
|
||||
+46
-6
@@ -67,13 +67,13 @@ String DTree::to_string()
|
||||
return("");
|
||||
}
|
||||
|
||||
String DTree::to_json()
|
||||
String DTree::to_json(char quote_char)
|
||||
{
|
||||
const DTree& target = deref();
|
||||
switch(target.type)
|
||||
{
|
||||
case('S'):
|
||||
return(json_escape(target._String));
|
||||
return(json_escape(target._String, quote_char));
|
||||
break;
|
||||
case('F'):
|
||||
return(std::to_string(target._float));
|
||||
@@ -121,6 +121,41 @@ String DTree::get_type_name()
|
||||
return("unknown");
|
||||
}
|
||||
|
||||
DTree DTree::get_by_path(String path, String delim)
|
||||
{
|
||||
const DTree* current = &deref();
|
||||
if(path == "")
|
||||
return(*current);
|
||||
size_t start = 0;
|
||||
while(start <= path.length())
|
||||
{
|
||||
size_t end = path.find(delim, start);
|
||||
String segment;
|
||||
if(end == String::npos)
|
||||
segment = path.substr(start);
|
||||
else
|
||||
segment = path.substr(start, end - start);
|
||||
if(segment == "")
|
||||
{
|
||||
if(end == String::npos)
|
||||
break;
|
||||
start = end + delim.length();
|
||||
continue;
|
||||
}
|
||||
current = ¤t->deref();
|
||||
if(current->type != 'M')
|
||||
return(DTree());
|
||||
auto it = current->_map.find(segment);
|
||||
if(it == current->_map.end())
|
||||
return(DTree());
|
||||
current = &it->second;
|
||||
if(end == String::npos)
|
||||
break;
|
||||
start = end + delim.length();
|
||||
}
|
||||
return(current->deref());
|
||||
}
|
||||
|
||||
bool DTree::is_reference()
|
||||
{
|
||||
return(type == 'R');
|
||||
@@ -374,16 +409,21 @@ String var_dump(DTree map, String prefix, String postfix)
|
||||
return(result);
|
||||
}
|
||||
|
||||
String json_escape(String s)
|
||||
String json_escape(String s, char quote_char)
|
||||
{
|
||||
//return(String("\"")+s+"\"");
|
||||
String result;
|
||||
u32 i = 0;
|
||||
result.append(1, '"');
|
||||
result.append(1, quote_char);
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
switch(c)
|
||||
if(c == quote_char)
|
||||
{
|
||||
result.append(1, '\\');
|
||||
result.append(1, quote_char);
|
||||
}
|
||||
else switch(c)
|
||||
{
|
||||
case('\t'):
|
||||
result.append("\\t");
|
||||
@@ -412,6 +452,6 @@ String json_escape(String s)
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
result.append(1, '"');
|
||||
result.append(1, quote_char);
|
||||
return(result);
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
String json_escape(String s);
|
||||
String json_escape(String s, char quote_char = '"');
|
||||
|
||||
struct DTree {
|
||||
|
||||
@@ -16,8 +16,9 @@ struct DTree {
|
||||
void each(std::function <void (DTree t, String key)> f);
|
||||
bool is_array();
|
||||
String to_string();
|
||||
String to_json();
|
||||
String to_json(char quote_char = '"');
|
||||
String get_type_name();
|
||||
DTree get_by_path(String path, String delim = "/");
|
||||
bool is_reference();
|
||||
DTree* reference_target();
|
||||
const DTree* reference_target() const;
|
||||
|
||||
+12
-2
@@ -359,6 +359,11 @@ String html_escape(f64 a)
|
||||
return(std::to_string(a));
|
||||
}
|
||||
|
||||
String json_encode(String s, char quote_char)
|
||||
{
|
||||
return(json_escape(s, quote_char));
|
||||
}
|
||||
|
||||
u64 int_val(String s, u32 base)
|
||||
{
|
||||
return(strtol(s.c_str(), 0, base));
|
||||
@@ -386,7 +391,7 @@ String nibble(String& haystack, String delim)
|
||||
}
|
||||
}
|
||||
|
||||
String json_encode(DTree t)
|
||||
String json_encode(DTree t, char quote_char)
|
||||
{
|
||||
String result = "";
|
||||
if(t.is_array())
|
||||
@@ -397,7 +402,7 @@ String json_encode(DTree t)
|
||||
if(count > 0)
|
||||
result += ", ";
|
||||
count += 1;
|
||||
result += json_escape(key) + ": " + json_encode(item);
|
||||
result += json_escape(key, quote_char) + ": " + json_encode(item, quote_char);
|
||||
});
|
||||
result += "}";
|
||||
}
|
||||
@@ -632,6 +637,11 @@ String ob_get_close()
|
||||
}
|
||||
|
||||
String safe_name(String raw)
|
||||
{
|
||||
return(ascii_safe_name(raw));
|
||||
}
|
||||
|
||||
String ascii_safe_name(String raw)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : raw)
|
||||
|
||||
@@ -80,7 +80,8 @@ String html_escape(String s);
|
||||
String html_escape(u64 a);
|
||||
String html_escape(f64 a);
|
||||
|
||||
String json_encode(DTree t);
|
||||
String json_encode(String s, char quote_char = '"');
|
||||
String json_encode(DTree t, char quote_char = '"');
|
||||
DTree json_decode(String s);
|
||||
|
||||
String var_dump(StringMap map, String prefix = "", String postfix = "\n");
|
||||
@@ -92,5 +93,6 @@ String ob_get();
|
||||
String ob_get_close();
|
||||
|
||||
String safe_name(String raw);
|
||||
String ascii_safe_name(String raw);
|
||||
|
||||
#define is_bit_set(var,pos) ((var) & (1<<(pos)))
|
||||
|
||||
@@ -129,6 +129,19 @@ String dirname(String fn)
|
||||
return(result);
|
||||
}
|
||||
|
||||
String path_join(String base, String child)
|
||||
{
|
||||
if(base == "")
|
||||
return(child);
|
||||
if(child == "")
|
||||
return(base);
|
||||
if(child[0] == '/')
|
||||
return(child);
|
||||
if(base[base.length() - 1] == '/')
|
||||
return(base + child);
|
||||
return(base + "/" + child);
|
||||
}
|
||||
|
||||
bool mkdir(String path)
|
||||
{
|
||||
shell_exec(String("mkdir -p ")+" "+shell_escape(path));
|
||||
|
||||
@@ -6,6 +6,7 @@ String shell_exec(String cmd);
|
||||
String shell_escape(String raw);
|
||||
String basename(String fn);
|
||||
String dirname(String fn);
|
||||
String path_join(String base, String child);
|
||||
bool mkdir(String path);
|
||||
bool file_exists(String path);
|
||||
String file_get_contents(String file_name);
|
||||
|
||||
@@ -14,6 +14,41 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace {
|
||||
|
||||
String http_status_reason(s32 code)
|
||||
{
|
||||
switch(code)
|
||||
{
|
||||
case 200: return("OK");
|
||||
case 201: return("Created");
|
||||
case 202: return("Accepted");
|
||||
case 204: return("No Content");
|
||||
case 301: return("Moved Permanently");
|
||||
case 302: return("Found");
|
||||
case 303: return("See Other");
|
||||
case 304: return("Not Modified");
|
||||
case 307: return("Temporary Redirect");
|
||||
case 308: return("Permanent Redirect");
|
||||
case 400: return("Bad Request");
|
||||
case 401: return("Unauthorized");
|
||||
case 403: return("Forbidden");
|
||||
case 404: return("Not Found");
|
||||
case 405: return("Method Not Allowed");
|
||||
case 409: return("Conflict");
|
||||
case 422: return("Unprocessable Content");
|
||||
case 429: return("Too Many Requests");
|
||||
case 500: return("Internal Server Error");
|
||||
case 501: return("Not Implemented");
|
||||
case 502: return("Bad Gateway");
|
||||
case 503: return("Service Unavailable");
|
||||
case 504: return("Gateway Timeout");
|
||||
default: return("");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SharedUnit::~SharedUnit()
|
||||
{
|
||||
if(so_handle)
|
||||
@@ -45,6 +80,17 @@ void Request::ob_start()
|
||||
ob = ob_stack.back();
|
||||
}
|
||||
|
||||
void Request::set_status(s32 code, String reason)
|
||||
{
|
||||
if(reason == "")
|
||||
reason = http_status_reason(code);
|
||||
String prefix = params["GATEWAY_INTERFACE"] != "" ? "Status: " : "HTTP/1.1 ";
|
||||
response_code = prefix + std::to_string(code);
|
||||
if(reason != "")
|
||||
response_code += " " + reason;
|
||||
flags.status = code;
|
||||
}
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
for(auto* stream : ob_stack)
|
||||
|
||||
@@ -80,6 +80,7 @@ struct SharedUnit {
|
||||
|
||||
request_handler on_setup;
|
||||
request_ref_handler on_render;
|
||||
request_ref_handler on_component;
|
||||
request_ref_handler on_websocket;
|
||||
|
||||
String compiler_messages;
|
||||
@@ -128,6 +129,7 @@ struct Request {
|
||||
StringMap session;
|
||||
|
||||
DTree var;
|
||||
DTree cfg;
|
||||
DTree call;
|
||||
DTree connection;
|
||||
|
||||
@@ -181,10 +183,12 @@ struct Request {
|
||||
u8 websocket_opcode = 0;
|
||||
bool websocket_is_binary = false;
|
||||
bool websocket_is_text = false;
|
||||
String current_unit_file = "";
|
||||
std::string params_buffer;
|
||||
} resources;
|
||||
|
||||
void ob_start();
|
||||
void set_status(s32 code, String reason = "");
|
||||
|
||||
~Request();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user