0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-09-29 20:28:52 +02:00

ircd: Various matrix library / modules / database development.

This commit is contained in:
Jason Volk 2017-08-23 15:51:34 -06:00
parent 10472684f2
commit 0149610bb2
16 changed files with 483 additions and 295 deletions

View file

@ -25,6 +25,8 @@
#pragma once
#define HAVE_IRCD_M_H
#include "json/parse.h"
namespace ircd {
namespace m {

View file

@ -33,8 +33,8 @@ struct error
{
template<class... args> error(const http::code &, const string_view &errcode, const char *const &fmt, args&&...);
template<class... args> error(const string_view &errcode, const char *const &fmt, args&&...);
error(const http::code &, const json::doc &doc = {});
error(const http::code &, const json::obj &obj);
error(const http::code &, const json::object &object = {});
error(const http::code &, const json::index &idx);
};
} // namespace m
@ -42,14 +42,14 @@ struct error
inline
ircd::m::error::error(const http::code &c,
const json::obj &obj)
:http::error{c, std::string{obj}}
const json::index &index)
:http::error{c, std::string{index}}
{}
inline
ircd::m::error::error(const http::code &c,
const json::doc &doc)
:http::error{c, std::string{doc}}
const json::object &object)
:http::error{c, std::string{object}}
{}
template<class... args>
@ -75,7 +75,7 @@ ircd::m::error::error(const http::code &status,
fmt::snprintf{estr, sizeof(estr), fmt, std::forward<args>(a)...}
};
return json::obj
return json::index
{
{ "errcode", errcode },
{ "error", string_view(estr, estr_len) }

View file

@ -22,16 +22,32 @@
*
*/
#include <ircd/json/object.h>
#pragma once
#define HAVE_IRCD_M_EVENT_H
///////////////////////////////////////////////////////////////////////////////
// Protocol notes
//
// 10.4
// The total size of any event MUST NOT exceed 65 KB.
//
// There are additional restrictions on sizes per key:
// sender MUST NOT exceed 255 bytes (including domain).
// room_id MUST NOT exceed 255 bytes.
// state_key MUST NOT exceed 255 bytes.
// type MUST NOT exceed 255 bytes.
// event_id MUST NOT exceed 255 bytes.
//
// Some event types have additional size restrictions which are specified in
// the description of the event. Additional keys have no limit other than that
// implied by the total 65 KB limit on events.
//
namespace ircd {
namespace m {
struct event
:json::object
:json::parse
<
string_view,
time_t,
@ -53,35 +69,7 @@ struct event
template<class... A>
event(A&&... a)
:object{std::make_tuple(std::forward<A>(a)...)}
{}
};
struct sync
:json::object
<
string_view,
string_view,
string_view,
string_view
>
{
IRCD_MEMBERS
(
"account_data",
"next_batch",
"rooms",
"presence",
)
sync(const json::doc &doc)
{
}
template<class... A>
sync(A&&... a)
:object{std::make_tuple(std::forward<A>(a)...)}
:parse{*this, std::forward<A>(a)...}
{}
};

View file

@ -29,7 +29,7 @@ namespace ircd {
namespace m {
struct request
:json::obj
:json::index
{
string_view method;
string_view path;
@ -39,12 +39,12 @@ struct request
request(const string_view &method,
const string_view &path,
const string_view &query = {},
std::initializer_list<json::obj::member> body = {});
std::initializer_list<json::index::member> body = {});
request(const string_view &method,
const string_view &path,
const string_view &query,
const json::doc &content);
const json::object &content);
};
} // namespace m
@ -54,8 +54,8 @@ inline
ircd::m::request::request(const string_view &method,
const string_view &path,
const string_view &query,
std::initializer_list<json::obj::member> body)
:json::obj{std::move(body)}
std::initializer_list<json::index::member> body)
:json::index{std::move(body)}
,method{method}
,path{path}
,query{query}
@ -66,8 +66,8 @@ inline
ircd::m::request::request(const string_view &method,
const string_view &path,
const string_view &query,
const json::doc &content)
:json::obj{content}
const json::object &content)
:json::index{content}
,method{method}
,path{path}
,query{query}

View file

@ -35,7 +35,7 @@ struct session
std::deque<std::string> tape;
std::multimap<string_view, string_view> resource;
json::doc operator()(parse::buffer &pb, request &);
json::object operator()(parse::buffer &pb, request &);
session(const host_port &);
};

View file

@ -26,7 +26,7 @@ ircd::m::session::session(const host_port &host_port)
{
}
ircd::json::doc
ircd::json::object
ircd::m::session::operator()(parse::buffer &pb,
request &r)
{
@ -49,20 +49,20 @@ ircd::m::session::operator()(parse::buffer &pb,
};
http::code status;
json::doc doc;
json::object object;
http::response
{
pc,
nullptr,
[&pc, &status, &doc](const http::response::head &head)
[&pc, &status, &object](const http::response::head &head)
{
status = http::status(head.status);
doc = http::response::content{pc, head};
object = http::response::content{pc, head};
}
};
if(status < 200 || status >= 300)
throw m::error(status, doc);
throw m::error(status, object);
return doc;
return object;
}

225
modules/client/events.cc Normal file
View file

@ -0,0 +1,225 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
const database::descriptor events_type_descriptor
{
// name
"type",
// explanation
R"(### protocol note:
10.1
The type of event. This SHOULD be namespaced similar to Java package naming conventions
e.g. 'com.example.subdomain.event.type'.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
}
};
const database::descriptor events_content_descriptor
{
// name
"content",
// explanation
R"(### protocol note:
10.1
The fields in this object will vary depending on the type of event. When interacting
with the REST API, this is the HTTP body.
### developer note:
Since events must not exceed 65 KB the maximum size for the content is the remaining
space after all the other fields for the event are rendered.
key is event_id
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
}
};
const database::descriptor events_room_id_descriptor
{
// name
"room_id",
// explanation
R"(### protocol note:
10.2 (apropos room events)
Required. The ID of the room associated with this event.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
}
};
const database::descriptor events_sender_descriptor
{
// name
"sender",
// explanation
R"(### protocol note:
10.2 (apropos room events)
Required. Contains the fully-qualified ID of the user who sent this event.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
}
};
const database::descriptor events_state_key_descriptor
{
// name
"state_key",
// explanation
R"(### protocol note:
10.3 (apropos room state events)
A unique key which defines the overwriting semantics for this piece of room state.
This value is often a zero-length string. The presence of this key makes this event a
State Event. The key MUST NOT start with '_'.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
}
};
const database::descriptor events_origin_descriptor
{
// name
"origin",
// explanation
R"(### protocol note:
FEDERATION 4.1
DNS name of homeserver that created this PDU
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
}
};
const database::descriptor events_origin_server_ts_descriptor
{
// name
"origin_server_ts",
// explanation
R"(### protocol note:
FEDERATION 4.1
Timestamp in milliseconds on origin homeserver when this PDU was created.
### developer note:
key is event_id
value is a machine integer (binary)
TODO: consider unsigned rather than time_t because of millisecond precision
)",
// typing (key, value)
{
typeid(string_view), typeid(time_t)
}
};
const database::description events_description
{
{ "default" },
events_type_descriptor,
events_content_descriptor,
events_room_id_descriptor,
events_sender_descriptor,
events_state_key_descriptor,
events_origin_descriptor,
events_origin_server_ts_descriptor,
};
std::shared_ptr<database> events_database
{
std::make_shared<database>("events"s, ""s, events_description)
};
extern database *const events
{
events_database.get()
};
resource events_resource
{
"_matrix/client/r0/events",
"Events (6.2.3) (10.x)"
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/events' and hosts the events database"
};

30
modules/client/events.h Normal file
View file

@ -0,0 +1,30 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
ircd::import_shared<ircd::database> events_database
{
"client_events", "events_database"
};
extern ircd::database *const events
{
events_database.get()
};

View file

@ -40,7 +40,7 @@ using object = db::object<account>;
template<class T = string_view> using value = db::value<T, account>;
resource::response
login_password(client &client, const resource::request &request)
post_login_password(client &client, const resource::request &request)
{
const auto user
{
@ -98,7 +98,7 @@ login_password(client &client, const resource::request &request)
}
resource::response
login_token(client &client, const resource::request &request)
post_login_token(client &client, const resource::request &request)
{
const auto token
{
@ -128,7 +128,7 @@ login_token(client &client, const resource::request &request)
}
resource::response
login(client &client, const resource::request &request)
post_login(client &client, const resource::request &request)
{
const auto type
{
@ -137,9 +137,9 @@ login(client &client, const resource::request &request)
};
if(type == "m.login.password")
return login_password(client, request);
return post_login_password(client, request);
else if(type == "m.login.token")
return login_token(client, request);
return post_login_token(client, request);
else
throw m::error
{
@ -147,9 +147,28 @@ login(client &client, const resource::request &request)
};
}
resource::method post
resource::method method_post
{
login_resource, "POST", login
login_resource, "POST", post_login
};
resource::response
get_login(client &client, const resource::request &request)
{
static const json::object flows
{
R"({"flows":[{"type":"m.login.password"}]})"
};
return resource::response
{
client, flows
};
}
resource::method method_get
{
login_resource, "GET", get_login
};
mapi::header IRCD_MODULE

View file

@ -19,7 +19,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "room.h"
#include "rooms.h"
using namespace ircd;
@ -33,49 +33,6 @@ resource publicrooms_resource
resource::response
get_publicrooms(client &client, const resource::request &request)
{
json::doc test
{
R"({"content":"hello","origin_server_ts":12345,"sender":"me"})"
};
m::event ev
{
"some content", 0, "some sender", "some type", "some unsigned", "statie"
};
json::for_each(ev, []
(const auto &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
});
std::cout << std::endl;
json::rfor_each(ev, []
(const string_view &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
});
std::cout << std::endl;
json::until(ev, []
(const string_view &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
return true;
});
std::cout << std::endl;
json::runtil(ev, []
(const string_view &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
return true;
});
std::cout << std::endl;
std::cout << index(ev, "origin_server_ts") << std::endl;
std::cout << std::endl;
return resource::response
{
client, json::index

View file

@ -89,10 +89,10 @@ handle_post(client &client,
"M_INVALID_USERNAME", "The desired user ID is not a valid user name."
};
const auto pass
const auto password
{
// "Required. The desired password for the account."
request.at("pass")
request.at("password")
};
const auto bind_email
@ -139,7 +139,7 @@ handle_post(client &client,
// Batch transaction to database
db::write
({
{ db::SET, password_text, pass },
{ db::SET, password_text, password },
{ db::SET, access_token_text, access_token },
});

View file

@ -1,173 +0,0 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
const database::descriptor room_created_descriptor
{
"created",
R"(### developer note:
A UNIX epoch timestamp sampled when the room was created.
)",
{
// readable key // binary value
typeid(string_view), typeid(time_t)
}
};
const database::descriptor room_creator_descriptor
{
"creator",
R"(### protocol note:
The user_id of the room creator. This is set by the homeserver.
)"
};
const database::descriptor room_topic_descriptor
{
"topic",
R"(### protocol note:
If this is included, an m.room.topic event will be sent into the room to indicate the
topic for the room. See Room Events for more information on m.room.topic.
)"
};
const database::descriptor room_visibility_descriptor
{
"visibility",
R"(### protocol note:
Rooms default to private visibility if this key is not included.
* "public" visibility indicates that the room will be shown in the published room list.
* "private" visibility will hide the room from the published room list.
One of: ["public", "private"]
)"
};
const database::descriptor room_join_rules_descriptor
{
"join_rules",
R"(### protocol note:
A room may be public meaning anyone can join the room without any prior action.
Alternatively, it can be invite meaning that a user who wishes to join the room must first
receive an invite to the room from someone already inside of the room. Currently, knock and private
are reserved keywords which are not implemented.
The type of rules used for users wishing to join this room.
One of: ["public", "knock", "invite", "private"]
)"
};
const database::descriptor room_history_visibility_descriptor
{
"history_visibility",
R"(### protocol note:
* "world_readable" All events while this is the m.room.history_visibility value may be shared
by any participating homeserver with anyone, regardless of whether they have ever joined the room.
* "shared" Previous events are always accessible to newly joined members. All events in the room
are accessible, even those sent when the member was not a part of the room.
* "invited" Events are accessible to newly joined members from the point they were invited onwards.
Events stop being accessible when the member's state changes to something other than invite or join.
* "joined" Events are accessible to newly joined members from the point they joined the room onwards.
Events stop being accessible when the member's state changes to something other than join.
)"
};
const database::descriptor room_alias_descriptor
{
"alias",
R"(### protocol note:
The desired room alias local part. If this is included, a room alias will be created and mapped to
the newly created room. The alias will belong on the same homeserver which created the room.
For example, if this was set to "foo" and sent to the homeserver "example.com" the complete
room alias would be #foo:example.com.
### developer note:
The alias column on the room's primary row has a comma separated list of aliases.
For each of those aliases this column has a key indexed for it; the value for that key is
the primary room's name. This is a cross-reference that must be kept in sync.
)"
};
const database::descriptor room_federate_descriptor
{
"federate",
R"(### protocol note:
Whether users on other servers can join this room. Defaults to true if key does not exist.
)",
{
// readable key // binary value
typeid(string_view), typeid(bool)
}
};
const database::description room_description
{
{ "default" },
room_created_descriptor,
room_creator_descriptor,
room_topic_descriptor,
room_visibility_descriptor,
room_join_rules_descriptor,
room_history_visibility_descriptor,
room_alias_descriptor,
room_federate_descriptor,
};
std::shared_ptr<database> room_database
{
std::make_shared<database>("room"s, ""s, room_description)
};
extern database *const room
{
room_database.get()
};
struct room
:resource
{
using resource::resource;
}
room_resource
{
"_matrix/client/r0/room",
"Rooms (7.0)"
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/room' to manage Matrix rooms"
};

76
modules/client/rooms.cc Normal file
View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
const database::descriptor rooms_head_descriptor
{
// name
"head",
// notes
R"(
### developer note:
The latest event for a room.
key is room_id
value is event_id
)",
// typing for key and value
{
typeid(string_view), typeid(string_view)
}
};
const database::description rooms_description
{
{ "default" },
rooms_head_descriptor,
};
std::shared_ptr<database> rooms_database
{
std::make_shared<database>("room"s, ""s, rooms_description)
};
extern database *const room
{
rooms_database.get()
};
struct room
:resource
{
using resource::resource;
}
rooms_resource
{
"_matrix/client/r0/rooms/",
"Rooms (7.0)"
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/rooms' and hosts the rooms database"
};

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
@ -19,12 +19,12 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
ircd::import_shared<ircd::database> room_database
ircd::import_shared<ircd::database> rooms_database
{
"client_room", "room_database"
"client_rooms", "rooms_database"
};
extern ircd::database *const room
extern ircd::database *const rooms
{
room_database.get()
rooms_database.get()
};

View file

@ -19,8 +19,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <ircd/listen.h>
using namespace ircd;
mapi::header IRCD_MODULE
@ -30,6 +28,8 @@ mapi::header IRCD_MODULE
std::map<std::string, module> modules;
void test();
//TODO: XXX very temporary stuff around here
// The path root (serves static assets for web etc); this pointer
// exists for now to easily find and reload that specifically.
@ -37,34 +37,97 @@ module *root_module;
const auto _init_([]
{
// These modules host databases and have to be loaded first.
modules.emplace("client_events.so"s, "client_events.so"s);
modules.emplace("client_account.so"s, "client_account.so"s);
modules.emplace("client_room.so"s, "client_room.so"s);
modules.emplace("client_rooms.so"s, "client_rooms.so"s);
for(const auto &name : mods::available())
if(name != "matrix.so")
modules.emplace(name, name);
root_module = &modules.at("root.so"s);
test();
return true;
}());
listener matrices
{
std::string { json::obj
std::string { json::index
{
{ "name", "Chat Matrix" },
{ "host", "0.0.0.0" },
{
"ssl_certificate_chain_file", "/home/jason/.synapse/zemos.net.crt"
"ssl_certificate_file", "/home/jason/newcert.pem"
},
{
"ssl_tmp_dh_file", "/home/jason/.synapse/cdc.z.tls.dh"
"ssl_certificate_chain_file", "/home/jason/newcert.pem"
},
{
"ssl_private_key_file_pem", "/home/jason/.synapse/cdc.z.tls.key"
"ssl_tmp_dh_file", "/home/jason/dh1024.pem"
},
{
"port", 6667
"ssl_private_key_file_pem", "/home/jason/privkey.pem"
},
{
"port", 8448
}
}}
};
void test()
{
json::object test
{
R"({"content":"hello","origin_server_ts":12345,"sender":"@foo:bar.com"})"
};
/*
const m::event ev0
{
"some content", 0, "some sender", "some type", "some unsigned", "statie"
};
*/
const m::event ev
{
test
};
std::cout << "size: " << sizeof(ev) << std::endl;
json::for_each(ev, []
(auto&& key, auto&& val)
{
std::cout << key << " => " << val << std::endl;
});
std::cout << "----" << std::endl;
json::rfor_each(ev, []
(const string_view &key, auto&& val)
{
std::cout << key << " => " << val << std::endl;
});
std::cout << "----" << std::endl;
json::until(ev, []
(const string_view &key, auto&& val)
{
std::cout << key << " => " << val << std::endl;
return true;
});
std::cout << "----" << std::endl;
json::runtil(ev, []
(const string_view &key, auto&& val)
{
std::cout << key << " => " << val << std::endl;
return true;
});
std::cout << std::endl;
std::cout << json::indexof(ev, "origin_server_ts") << std::endl;
std::cout << std::endl;
}

View file

@ -37,9 +37,10 @@ __attribute__((constructor))
void
init_0()
{
for(const auto &file : fs::ls("/home/jason/charybdis/charybdis/modules/static"))
// TODO: XXX
for(const auto &file : fs::ls_recursive("/home/jason/charybdis/charybdis/modules/static"))
{
const auto name(token_last(file, "/"));
const auto name(tokens_after(file, "/", 5));
files.emplace(std::string(name), file);
}
}