mirror of
https://github.com/matrix-construct/construct
synced 2024-11-25 08:12:37 +01:00
ircd::json: Improve tuple/builder related.
This commit is contained in:
parent
db748cae81
commit
e95fe7fbd9
6 changed files with 554 additions and 197 deletions
|
@ -96,8 +96,8 @@ namespace ircd::json
|
|||
#include "json/member.h"
|
||||
#include "json/index.h"
|
||||
#include "json/property.h"
|
||||
#include "json/tuple.h"
|
||||
#include "json/builder.h"
|
||||
#include "json/tuple.h"
|
||||
|
||||
namespace ircd
|
||||
{
|
||||
|
|
|
@ -22,153 +22,197 @@
|
|||
#pragma once
|
||||
#define HAVE_IRCD_JSON_BUILDER_H
|
||||
|
||||
// ircd::json::builder is an interface to compose JSON dynamically and
|
||||
// efficiently. The product of the builder is an iteration of the added members
|
||||
// for use by stringifying and iovectoring. This gathers the members on a trip
|
||||
// up the stack without rewriting a JSON string at each frame.
|
||||
// ircd::json::builder is forward list to compose JSON dynamically and
|
||||
// efficiently on the stack. The product of the builder is an iteration of the
|
||||
// added members for use by stringifying and iovectoring. This gathers the
|
||||
// members on a trip up the stack without rewriting a JSON string at each frame.
|
||||
//
|
||||
struct ircd::json::builder
|
||||
namespace ircd::json
|
||||
{
|
||||
struct builder;
|
||||
|
||||
using member_closure = std::function<void (const member &)>;
|
||||
using member_closure_bool = std::function<bool (const member &)>;
|
||||
using builder_closure_bool = std::function<bool (const builder &)>;
|
||||
|
||||
builder *head(builder *const &);
|
||||
builder *next(builder *const &);
|
||||
builder *prev(builder *const &);
|
||||
builder *tail(builder *);
|
||||
builder *find(builder *, const builder_closure_bool &);
|
||||
|
||||
const builder *head(const builder *const &);
|
||||
const builder *next(const builder *const &);
|
||||
const builder *prev(const builder *const &);
|
||||
const builder *tail(const builder *);
|
||||
const builder *find(const builder *, const builder_closure_bool &);
|
||||
|
||||
void for_each(const builder &, const member_closure &);
|
||||
bool until(const builder &, const member_closure_bool &);
|
||||
size_t count(const builder &, const member_closure_bool &);
|
||||
size_t count(const builder &);
|
||||
}
|
||||
|
||||
struct ircd::json::builder
|
||||
{
|
||||
struct add;
|
||||
struct set;
|
||||
struct push;
|
||||
|
||||
IRCD_EXCEPTION(json::error, error);
|
||||
IRCD_EXCEPTION(error, exists);
|
||||
|
||||
const builder *parent;
|
||||
member m;
|
||||
const members *ms;
|
||||
builder *head;
|
||||
builder *child;
|
||||
|
||||
void for_each(const member_closure &) const;
|
||||
bool until(const member_closure_bool &) const;
|
||||
size_t count(const member_closure_bool &) const;
|
||||
size_t count() const;
|
||||
bool test(const member_closure_bool &) const;
|
||||
|
||||
// recursive!
|
||||
const member *find(const string_view &key) const;
|
||||
const json::value &at(const string_view &key) const;
|
||||
bool has(const string_view &key) const;
|
||||
|
||||
builder(const builder *const &parent, const members *const &);
|
||||
builder(const builder *const &parent, member);
|
||||
builder(member m = {},
|
||||
const members *const &ms = nullptr,
|
||||
builder *const &head = nullptr,
|
||||
builder *const &child = nullptr)
|
||||
:m{std::move(m)}
|
||||
,ms{ms}
|
||||
,head{head}
|
||||
,child{child}
|
||||
{}
|
||||
|
||||
builder(const members &ms)
|
||||
:builder{{}, &ms}
|
||||
{}
|
||||
|
||||
builder(member m)
|
||||
:builder{std::move(m)}
|
||||
{}
|
||||
|
||||
friend string_view stringify(mutable_buffer &, const builder &);
|
||||
};
|
||||
|
||||
inline ircd::string_view
|
||||
ircd::json::stringify(mutable_buffer &head,
|
||||
const builder &builder)
|
||||
struct ircd::json::builder::push
|
||||
:private ircd::json::builder
|
||||
{
|
||||
const auto num{builder.count()};
|
||||
const member *m[num];
|
||||
|
||||
size_t i(0);
|
||||
builder.for_each([&i, &m]
|
||||
(const auto &member)
|
||||
push(builder &head, const members &ms)
|
||||
:builder{{}, &ms, &head}
|
||||
{
|
||||
m[i++] = &member;
|
||||
});
|
||||
tail(&head)->child = this;
|
||||
}
|
||||
|
||||
return stringify(head, m, m + num);
|
||||
}
|
||||
|
||||
inline
|
||||
ircd::json::builder::builder(const builder *const &parent,
|
||||
member m)
|
||||
:parent{parent}
|
||||
,m{std::move(m)}
|
||||
,ms{nullptr}
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
ircd::json::builder::builder(const builder *const &parent,
|
||||
const members *const &ms)
|
||||
:parent{parent}
|
||||
,m{}
|
||||
,ms{ms}
|
||||
{
|
||||
}
|
||||
|
||||
inline const ircd::json::value &
|
||||
ircd::json::builder::at(const string_view &key)
|
||||
const
|
||||
{
|
||||
const auto *const member(find(key));
|
||||
if(!member)
|
||||
throw not_found("'%s'", key);
|
||||
|
||||
return member->second;
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ircd::json::builder::count()
|
||||
const
|
||||
{
|
||||
return count([]
|
||||
(const auto &)
|
||||
push(builder &head, member m)
|
||||
:builder{std::move(m), nullptr, &head}
|
||||
{
|
||||
return true;
|
||||
});
|
||||
tail(&head)->child = this;
|
||||
}
|
||||
};
|
||||
|
||||
struct ircd::json::builder::add
|
||||
:private ircd::json::builder
|
||||
{
|
||||
add(builder &head, const members &ms);
|
||||
add(builder &head, member member);
|
||||
};
|
||||
|
||||
struct ircd::json::builder::set
|
||||
:private ircd::json::builder
|
||||
{
|
||||
set(builder &head, const members &ms);
|
||||
set(builder &head, member member);
|
||||
};
|
||||
|
||||
inline ircd::json::builder *
|
||||
ircd::json::find(builder *builder,
|
||||
const builder_closure_bool &test)
|
||||
{
|
||||
for(; builder; builder = next(builder))
|
||||
if(test(*builder))
|
||||
return builder;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ircd::json::builder::count(const member_closure_bool &closure)
|
||||
const
|
||||
inline const ircd::json::builder *
|
||||
ircd::json::find(const builder *builder,
|
||||
const builder_closure_bool &test)
|
||||
{
|
||||
size_t ret(0);
|
||||
for_each([&closure, &ret]
|
||||
(const auto &member)
|
||||
{
|
||||
ret += closure(member);
|
||||
});
|
||||
for(; builder; builder = next(builder))
|
||||
if(test(*builder))
|
||||
return builder;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline ircd::json::builder *
|
||||
ircd::json::tail(builder *ret)
|
||||
{
|
||||
while(ret && next(ret))
|
||||
ret = next(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline const ircd::json::member *
|
||||
ircd::json::builder::find(const string_view &key)
|
||||
const
|
||||
inline const ircd::json::builder *
|
||||
ircd::json::tail(const builder *ret)
|
||||
{
|
||||
const member *ret;
|
||||
const auto test
|
||||
{
|
||||
[&key, &ret](const auto &member)
|
||||
{
|
||||
if(key == string_view{member.first})
|
||||
{
|
||||
ret = &member;
|
||||
return false;
|
||||
}
|
||||
else return true;
|
||||
}
|
||||
};
|
||||
while(ret && next(ret))
|
||||
ret = next(ret);
|
||||
|
||||
return !until(test)? ret : nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void
|
||||
ircd::json::builder::for_each(const member_closure &closure)
|
||||
const
|
||||
inline ircd::json::builder *
|
||||
ircd::json::prev(builder *const &builder)
|
||||
{
|
||||
if(ms)
|
||||
std::for_each(begin(*ms), end(*ms), closure);
|
||||
else
|
||||
closure(m);
|
||||
assert(builder);
|
||||
auto *ret(builder->head);
|
||||
for(; ret; ret = next(ret))
|
||||
if(next(ret) == builder)
|
||||
return ret;
|
||||
|
||||
if(parent)
|
||||
parent->for_each(closure);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::json::builder::until(const member_closure_bool &closure)
|
||||
const
|
||||
inline const ircd::json::builder *
|
||||
ircd::json::prev(const builder *const &builder)
|
||||
{
|
||||
if(ms)
|
||||
{
|
||||
if(!ircd::until(begin(*ms), end(*ms), closure))
|
||||
return false;
|
||||
}
|
||||
else if(!closure(m))
|
||||
return false;
|
||||
assert(builder);
|
||||
const auto *ret(builder->head);
|
||||
for(; ret; ret = next(ret))
|
||||
if(next(ret) == builder)
|
||||
return ret;
|
||||
|
||||
if(parent)
|
||||
return parent->until(closure);
|
||||
|
||||
return true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline ircd::json::builder *
|
||||
ircd::json::next(builder *const &builder)
|
||||
{
|
||||
assert(builder);
|
||||
return builder->child;
|
||||
}
|
||||
|
||||
inline const ircd::json::builder *
|
||||
ircd::json::next(const builder *const &builder)
|
||||
{
|
||||
assert(builder);
|
||||
return builder->child;
|
||||
}
|
||||
|
||||
inline ircd::json::builder *
|
||||
ircd::json::head(builder *const &builder)
|
||||
{
|
||||
assert(builder);
|
||||
return builder->head;
|
||||
}
|
||||
|
||||
inline const ircd::json::builder *
|
||||
ircd::json::head(const builder *const &builder)
|
||||
{
|
||||
assert(builder);
|
||||
return builder->head;
|
||||
}
|
||||
|
|
|
@ -51,10 +51,15 @@ namespace json {
|
|||
// ircd::json::tuple template. Create your own struct inheriting this
|
||||
// class template with the members.
|
||||
//
|
||||
struct tuple_base
|
||||
{
|
||||
// EBO tag
|
||||
};
|
||||
|
||||
template<class... T>
|
||||
struct tuple
|
||||
:std::tuple<T...>
|
||||
,tuple_base
|
||||
{
|
||||
using tuple_type = std::tuple<T...>;
|
||||
using super_type = tuple<T...>;
|
||||
|
@ -62,10 +67,21 @@ struct tuple
|
|||
static constexpr size_t size();
|
||||
|
||||
tuple(const json::object &);
|
||||
tuple(const json::builder &);
|
||||
tuple(const std::initializer_list<member> &);
|
||||
tuple() = default;
|
||||
};
|
||||
|
||||
template<class tuple>
|
||||
constexpr bool
|
||||
is_tuple()
|
||||
{
|
||||
return std::is_base_of<tuple_base, tuple>::value;
|
||||
}
|
||||
|
||||
template<class tuple, class R>
|
||||
using enable_if_tuple = typename std::enable_if<is_tuple<tuple>(), R>::type;
|
||||
|
||||
template<class tuple>
|
||||
using tuple_type = typename tuple::tuple_type;
|
||||
|
||||
|
@ -102,7 +118,7 @@ stdcast(tuple &o)
|
|||
}
|
||||
|
||||
template<class tuple>
|
||||
constexpr size_t
|
||||
constexpr enable_if_tuple<tuple, size_t>
|
||||
size()
|
||||
{
|
||||
return tuple_size<tuple>::value;
|
||||
|
@ -110,18 +126,18 @@ size()
|
|||
|
||||
template<class tuple,
|
||||
size_t i>
|
||||
constexpr auto &
|
||||
constexpr enable_if_tuple<tuple, const char *const &>
|
||||
key()
|
||||
{
|
||||
return tuple_element<tuple, i>::key;
|
||||
}
|
||||
|
||||
template<size_t i,
|
||||
class... T>
|
||||
auto &
|
||||
key(const tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, const char *const &>
|
||||
key(const tuple &t)
|
||||
{
|
||||
return get<i>(t).key;
|
||||
return std::get<i>(t).key;
|
||||
}
|
||||
|
||||
template<class tuple,
|
||||
|
@ -167,65 +183,63 @@ indexof(const string_view &name)
|
|||
}
|
||||
|
||||
template<size_t i,
|
||||
class... T>
|
||||
auto &
|
||||
get(const tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, tuple_value_type<tuple, i> &>
|
||||
get(tuple &t)
|
||||
{
|
||||
return std::get<i>(t);
|
||||
}
|
||||
|
||||
template<size_t i,
|
||||
class... T>
|
||||
auto &
|
||||
get(tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, const tuple_value_type<tuple, i> &>
|
||||
get(const tuple &t)
|
||||
{
|
||||
return std::get<i>(t);
|
||||
}
|
||||
|
||||
template<size_t i,
|
||||
class... T>
|
||||
auto &
|
||||
val(const tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, const tuple_value_type<tuple, i> &>
|
||||
val(const tuple &t)
|
||||
{
|
||||
using value_type = tuple_value_type<tuple<T...>, i>;
|
||||
|
||||
using value_type = tuple_value_type<tuple, i>;
|
||||
return static_cast<const value_type &>(get<i>(t));
|
||||
}
|
||||
|
||||
template<size_t i,
|
||||
class... T>
|
||||
auto &
|
||||
val(tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, tuple_value_type<tuple, i> &>
|
||||
val(tuple &t)
|
||||
{
|
||||
using value_type = tuple_value_type<tuple<T...>, i>;
|
||||
|
||||
using value_type = tuple_value_type<tuple, i>;
|
||||
return static_cast<value_type &>(get<i>(t));
|
||||
}
|
||||
|
||||
template<const char *const &name,
|
||||
class... T>
|
||||
auto &
|
||||
val(const tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, const tuple_value_type<tuple, indexof<tuple>(name)> &>
|
||||
val(const tuple &t)
|
||||
{
|
||||
return val<indexof<tuple<T...>>(name)>(t);
|
||||
return val<indexof<tuple>(name)>(t);
|
||||
}
|
||||
|
||||
template<const char *const &name,
|
||||
class... T>
|
||||
auto &
|
||||
val(tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, tuple_value_type<tuple, indexof<tuple>(name)> &>
|
||||
val(tuple &t)
|
||||
{
|
||||
return val<indexof<tuple<T...>>(name)>(t);
|
||||
return val<indexof<tuple>(name)>(t);
|
||||
}
|
||||
|
||||
template<const char *const &name,
|
||||
class... T>
|
||||
const tuple_value_type<tuple<T...>, indexof<tuple<T...>>(name)> &
|
||||
at(const tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, const tuple_value_type<tuple, indexof<tuple>(name)> &>
|
||||
at(const tuple &t)
|
||||
{
|
||||
constexpr size_t idx
|
||||
{
|
||||
indexof<tuple<T...>>(name)
|
||||
indexof<tuple>(name)
|
||||
};
|
||||
|
||||
auto &ret
|
||||
|
@ -233,7 +247,7 @@ at(const tuple<T...> &t)
|
|||
val<idx>(t)
|
||||
};
|
||||
|
||||
using value_type = tuple_value_type<tuple<T...>, idx>;
|
||||
using value_type = tuple_value_type<tuple, idx>;
|
||||
|
||||
//TODO: does tcmalloc zero this or huh?
|
||||
if(ret == value_type{})
|
||||
|
@ -243,13 +257,13 @@ at(const tuple<T...> &t)
|
|||
}
|
||||
|
||||
template<const char *const &name,
|
||||
class... T>
|
||||
tuple_value_type<tuple<T...>, indexof<tuple<T...>>(name)> &
|
||||
at(tuple<T...> &t)
|
||||
class tuple>
|
||||
enable_if_tuple<tuple, tuple_value_type<tuple, indexof<tuple>(name)> &>
|
||||
at(tuple &t)
|
||||
{
|
||||
constexpr size_t idx
|
||||
{
|
||||
indexof<tuple<T...>>(name)
|
||||
indexof<tuple>(name)
|
||||
};
|
||||
|
||||
auto &ret
|
||||
|
@ -257,7 +271,7 @@ at(tuple<T...> &t)
|
|||
val<idx>(t)
|
||||
};
|
||||
|
||||
using value_type = tuple_value_type<tuple<T...>, idx>;
|
||||
using value_type = tuple_value_type<tuple, idx>;
|
||||
|
||||
//TODO: does tcmalloc zero this or huh?
|
||||
if(ret == value_type{})
|
||||
|
@ -565,6 +579,31 @@ tuple<T...>::tuple(const json::object &object)
|
|||
});
|
||||
}
|
||||
|
||||
template<class... T>
|
||||
tuple<T...>::tuple(const json::builder &builder)
|
||||
{
|
||||
//TODO: is tcmalloc zero-initializing all tuple elements, or is something else doing that?
|
||||
for_each(builder, [this]
|
||||
(const auto &member)
|
||||
{
|
||||
at(*this, member.first, [&member]
|
||||
(auto &target)
|
||||
{
|
||||
using target_type = decltype(target);
|
||||
using cast_type = typename std::remove_reference<target_type>::type; try
|
||||
{
|
||||
target = static_cast<cast_type>(member.second);
|
||||
}
|
||||
catch(const bad_lex_cast &e)
|
||||
{
|
||||
throw parse_error("member '%s' must convert to '%s'",
|
||||
member.first,
|
||||
typeid(target_type).name());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
template<class... T>
|
||||
tuple<T...>::tuple(const std::initializer_list<member> &members)
|
||||
{
|
||||
|
@ -748,7 +787,7 @@ template<class... T>
|
|||
std::ostream &
|
||||
operator<<(std::ostream &s, const tuple<T...> &t)
|
||||
{
|
||||
s << string(t);
|
||||
s << json::string(t);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@ struct ircd::json::value
|
|||
bool empty() const;
|
||||
size_t serialized() const;
|
||||
operator string_view() const;
|
||||
explicit operator double() const;
|
||||
explicit operator int64_t() const;
|
||||
explicit operator std::string() const;
|
||||
|
||||
template<class T> explicit value(const T &specialized);
|
||||
|
|
267
ircd/json.cc
267
ircd/json.cc
|
@ -443,6 +443,13 @@ ircd::json::stringify(mutable_buffer &buf,
|
|||
return stringify(buf, begin, end);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::json::stringify(mutable_buffer &buf,
|
||||
const member &m)
|
||||
{
|
||||
return stringify(buf, &m, &m + 1);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::json::stringify(mutable_buffer &buf,
|
||||
const member *const &begin,
|
||||
|
@ -992,6 +999,46 @@ const
|
|||
throw type_error("value type[%d] is not a string", int(type));
|
||||
}
|
||||
|
||||
ircd::json::value::operator int64_t()
|
||||
const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case NUMBER:
|
||||
return likely(!floats)? integer : floating;
|
||||
|
||||
case STRING:
|
||||
return lex_cast<int64_t>(string_view(*this));
|
||||
|
||||
case ARRAY:
|
||||
case OBJECT:
|
||||
case LITERAL:
|
||||
break;
|
||||
}
|
||||
|
||||
throw type_error("value type[%d] is not an int64_t", int(type));
|
||||
}
|
||||
|
||||
ircd::json::value::operator double()
|
||||
const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case NUMBER:
|
||||
return likely(floats)? floating : integer;
|
||||
|
||||
case STRING:
|
||||
return lex_cast<double>(string_view(*this));
|
||||
|
||||
case ARRAY:
|
||||
case OBJECT:
|
||||
case LITERAL:
|
||||
break;
|
||||
}
|
||||
|
||||
throw type_error("value type[%d] is not a float", int(type));
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::json::value::empty()
|
||||
const
|
||||
|
@ -1364,3 +1411,223 @@ ircd::json::type(const string_view &buf,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// builder.h
|
||||
//
|
||||
|
||||
ircd::string_view
|
||||
ircd::json::stringify(mutable_buffer &head,
|
||||
const builder &builder)
|
||||
{
|
||||
const auto num{count(builder)};
|
||||
const member *m[num];
|
||||
|
||||
size_t i(0);
|
||||
for_each(builder, [&i, &m]
|
||||
(const auto &member)
|
||||
{
|
||||
m[i++] = &member;
|
||||
});
|
||||
|
||||
return stringify(head, m, m + num);
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::json::count(const builder &builder)
|
||||
{
|
||||
return count(builder, []
|
||||
(const auto &)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::json::count(const builder &builder,
|
||||
const member_closure_bool &closure)
|
||||
{
|
||||
size_t ret(0);
|
||||
for_each(builder, [&closure, &ret]
|
||||
(const auto &member)
|
||||
{
|
||||
ret += closure(member);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::json::for_each(const builder &b,
|
||||
const member_closure &closure)
|
||||
{
|
||||
if(b.ms)
|
||||
std::for_each(begin(*b.ms), end(*b.ms), closure);
|
||||
else if(!b.m.first.empty())
|
||||
closure(b.m);
|
||||
|
||||
if(b.child)
|
||||
for_each(*b.child, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::json::until(const builder &b,
|
||||
const member_closure_bool &closure)
|
||||
{
|
||||
if(b.ms)
|
||||
{
|
||||
if(!ircd::until(begin(*b.ms), end(*b.ms), closure))
|
||||
return false;
|
||||
}
|
||||
else if(!b.m.first.empty() && !closure(b.m))
|
||||
return false;
|
||||
|
||||
if(b.child)
|
||||
return until(*b.child, closure);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::json::builder::has(const string_view &key)
|
||||
const
|
||||
{
|
||||
const auto *const member(find(key));
|
||||
return member != nullptr;
|
||||
}
|
||||
|
||||
const ircd::json::value &
|
||||
ircd::json::builder::at(const string_view &key)
|
||||
const
|
||||
{
|
||||
const auto *const member(find(key));
|
||||
if(!member)
|
||||
throw not_found("'%s'", key);
|
||||
|
||||
return member->second;
|
||||
}
|
||||
|
||||
const ircd::json::member *
|
||||
ircd::json::builder::find(const string_view &key)
|
||||
const
|
||||
{
|
||||
const member *ret;
|
||||
const auto test
|
||||
{
|
||||
[&key, &ret](const auto &member) -> bool
|
||||
{
|
||||
if(key == string_view{member.first})
|
||||
{
|
||||
ret = &member;
|
||||
return false;
|
||||
}
|
||||
else return true;
|
||||
}
|
||||
};
|
||||
|
||||
return !until(*this, test)? ret : nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::json::builder::test(const member_closure_bool &closure)
|
||||
const
|
||||
{
|
||||
if(ms)
|
||||
return ircd::until(begin(*ms), end(*ms), closure);
|
||||
|
||||
if(!m.first.empty())
|
||||
return closure(m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ircd::json::builder::add::add(builder &head, const members &ms)
|
||||
:builder{{}, &ms, &head}
|
||||
{
|
||||
const auto existing(json::find(&head, [&ms](const builder &existing)
|
||||
{
|
||||
return existing.test([&ms](const member &existing)
|
||||
{
|
||||
return std::all_of(begin(ms), end(ms), [&existing]
|
||||
(const auto &member)
|
||||
{
|
||||
return member.first == existing.first;
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
if(existing)
|
||||
throw exists("failed to add member '%s': already exists",
|
||||
string_view{existing->m.first}); //TODO BUG
|
||||
|
||||
tail(&head)->child = this;
|
||||
}
|
||||
|
||||
ircd::json::builder::add::add(builder &head, member member)
|
||||
:builder{std::move(member), nullptr, &head}
|
||||
{
|
||||
const auto existing(json::find(&head, [&member](const builder &existing)
|
||||
{
|
||||
return existing.test([&member](const auto &existing)
|
||||
{
|
||||
return member.first == existing.first;
|
||||
});
|
||||
}));
|
||||
|
||||
if(existing)
|
||||
throw exists("failed to add member '%s': already exists",
|
||||
string_view{member.first});
|
||||
|
||||
tail(&head)->child = this;
|
||||
}
|
||||
|
||||
ircd::json::builder::set::set(builder &head, const members &ms)
|
||||
:builder{{}, &ms, &head}
|
||||
{
|
||||
const auto existing(json::find(&head, [&ms](const builder &existing)
|
||||
{
|
||||
return existing.test([&ms](const member &existing)
|
||||
{
|
||||
return std::all_of(begin(ms), end(ms), [&existing]
|
||||
(const auto &member)
|
||||
{
|
||||
return member.first == existing.first;
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
if(existing)
|
||||
{
|
||||
const auto p(prev(existing));
|
||||
if(p)
|
||||
{
|
||||
p->child = this;
|
||||
this->child = existing->child;
|
||||
}
|
||||
}
|
||||
else tail(&head)->child = this;
|
||||
}
|
||||
|
||||
ircd::json::builder::set::set(builder &head, member member)
|
||||
:builder{std::move(member), nullptr, &head}
|
||||
{
|
||||
const auto existing(json::find(&head, [&member](const builder &existing)
|
||||
{
|
||||
return existing.test([&member](const auto &existing)
|
||||
{
|
||||
return member.first == existing.first;
|
||||
});
|
||||
}));
|
||||
|
||||
if(existing)
|
||||
{
|
||||
const auto p(prev(existing));
|
||||
if(p)
|
||||
{
|
||||
p->child = this;
|
||||
this->child = existing->child;
|
||||
}
|
||||
}
|
||||
else tail(&head)->child = this;
|
||||
}
|
||||
|
|
|
@ -248,14 +248,33 @@ namespace ircd::m::events
|
|||
ircd::database *ircd::m::events::events;
|
||||
|
||||
void
|
||||
ircd::m::events::insert(const event &a)
|
||||
ircd::m::events::insert(json::builder &builder)
|
||||
{
|
||||
event b(a);
|
||||
insert(b);
|
||||
const id::event::buf generated_event_id
|
||||
{
|
||||
builder.has("event_id")? id::event::buf{} : id::event::buf{id::generate, "cdc.z"}
|
||||
};
|
||||
|
||||
const json::builder::add event_id
|
||||
{
|
||||
builder, { "event_id", generated_event_id }
|
||||
};
|
||||
|
||||
const json::builder::set event_id2
|
||||
{
|
||||
builder, { "event_id", generated_event_id }
|
||||
};
|
||||
|
||||
const json::builder::add origin_server_ts
|
||||
{
|
||||
builder, { "origin_server_ts", time<milliseconds>() }
|
||||
};
|
||||
|
||||
insert(event{builder});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::events::insert(event &event)
|
||||
ircd::m::events::insert(const event &event)
|
||||
{
|
||||
if(!json::val<name::type>(event))
|
||||
throw BAD_JSON("Required event field: '%s'", name::type);
|
||||
|
@ -263,32 +282,11 @@ ircd::m::events::insert(event &event)
|
|||
if(!json::val<name::sender>(event))
|
||||
throw BAD_JSON("Required event field: '%s'", name::sender);
|
||||
|
||||
bool has_event_id
|
||||
{
|
||||
!json::val<name::event_id>(event).empty()
|
||||
};
|
||||
if(!json::val<name::event_id>(event))
|
||||
throw BAD_JSON("Required event field: '%s'", name::event_id);
|
||||
|
||||
const id::event::buf generated_event_id
|
||||
{
|
||||
has_event_id? id::event::buf{} : id::event::buf{id::generate, "cdc.z"}
|
||||
};
|
||||
|
||||
if(!has_event_id)
|
||||
json::val<name::event_id>(event) = generated_event_id;
|
||||
|
||||
const scope remove_our_event_id([&]
|
||||
{
|
||||
if(!has_event_id)
|
||||
json::val<name::event_id>(event) = {};
|
||||
});
|
||||
|
||||
bool has_origin_server_ts
|
||||
{
|
||||
json::val<name::origin_server_ts>(event) != 0
|
||||
};
|
||||
|
||||
if(!has_origin_server_ts)
|
||||
json::val<name::origin_server_ts>(event) = time<milliseconds>();
|
||||
if(!json::val<name::origin_server_ts>(event))
|
||||
throw BAD_JSON("Required event field: '%s'", name::origin_server_ts);
|
||||
|
||||
for(const auto &transition : events::transitions)
|
||||
if(!transition->valid(event))
|
||||
|
@ -410,20 +408,19 @@ ircd::m::events::write(const event &event)
|
|||
|
||||
void
|
||||
ircd::m::room::join(const m::id::user &user_id,
|
||||
const json::builder &content)
|
||||
json::builder &content)
|
||||
{
|
||||
const json::builder content_with_membership
|
||||
json::builder::set membership_join
|
||||
{
|
||||
&content,
|
||||
{ "membership", "join" }
|
||||
content, { "membership", "join" }
|
||||
};
|
||||
|
||||
membership(user_id, content_with_membership);
|
||||
membership(user_id, content);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::membership(const m::id::user &user_id,
|
||||
const json::builder &content)
|
||||
json::builder &content)
|
||||
{
|
||||
if(is_member(user_id, content.at("membership")))
|
||||
throw m::ALREADY_MEMBER
|
||||
|
@ -431,15 +428,23 @@ ircd::m::room::membership(const m::id::user &user_id,
|
|||
"Already a member with this membership."
|
||||
};
|
||||
|
||||
char cbuf[512];
|
||||
m::events::insert(m::event
|
||||
char buffer[512];
|
||||
const auto printed_content
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id },
|
||||
{ "sender", user_id },
|
||||
{ "content", stringify(cbuf, content) }
|
||||
});
|
||||
stringify(buffer, content)
|
||||
};
|
||||
|
||||
json::builder event;
|
||||
json::builder::set fields{event,
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id },
|
||||
{ "sender", user_id },
|
||||
{ "content", printed_content }
|
||||
}};
|
||||
|
||||
m::events::insert(event);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
Loading…
Reference in a new issue