LCOV - code coverage report
Current view: top level - include/boost/http/db - schema.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 38 38
Test Date: 2026-02-09 01:37:05 Functions: 100.0 % 28 28

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0.
       5              : // https://www.boost.org/LICENSE_1_0.txt
       6              : //
       7              : 
       8              : #ifndef BOOST_HTTP_DB_SCHEMA_HPP
       9              : #define BOOST_HTTP_DB_SCHEMA_HPP
      10              : 
      11              : #include <cstdint>
      12              : #include <tuple>
      13              : #include <type_traits>
      14              : #include <string_view>
      15              : 
      16              : namespace boost {
      17              : namespace http {
      18              : namespace db {
      19              : 
      20              : /** Bitwise flags describing column properties.
      21              : 
      22              :     Accumulated on a @ref field_t descriptor via
      23              :     builder-style member functions.
      24              : */
      25              : enum field_flags : unsigned
      26              : {
      27              :     flag_none           = 0,
      28              :     flag_primary_key    = 1 << 0,
      29              :     flag_auto_increment = 1 << 1,
      30              :     flag_not_null       = 1 << 2,
      31              :     flag_unique         = 1 << 3,
      32              :     flag_indexed        = 1 << 4
      33              : };
      34              : 
      35              : /** Describe a single column mapped to a struct member.
      36              : 
      37              :     The member pointer is stored as a data member so
      38              :     that the natural syntax works without requiring
      39              :     angle brackets. The entire schema description is
      40              :     constexpr and available at compile time.
      41              : 
      42              :     @par Example
      43              :     @code
      44              :     field("id", &user::id).primary_key().auto_increment()
      45              :     field("email", &user::email).not_null().unique()
      46              :     @endcode
      47              : 
      48              :     @tparam T Member value type (e.g. `std::string`).
      49              :     @tparam C Containing class type (e.g. `user`).
      50              : 
      51              :     @see field, embed_t, has_one_t
      52              : */
      53              : template <typename T, typename C>
      54              : struct field_t
      55              : {
      56              :     using value_type = T;
      57              :     using class_type = C;
      58              : 
      59              :     std::string_view name;
      60              :     T C::* pointer;
      61              :     unsigned flags = flag_none;
      62              : 
      63              :     /// Mark this column as the primary key.
      64              :     constexpr field_t& primary_key()    { flags |= flag_primary_key;    return *this; }
      65              : 
      66              :     /// Mark this column as auto-incrementing.
      67              :     constexpr field_t& auto_increment() { flags |= flag_auto_increment; return *this; }
      68              : 
      69              :     /// Mark this column as NOT NULL.
      70              :     constexpr field_t& not_null()       { flags |= flag_not_null;       return *this; }
      71              : 
      72              :     /// Mark this column as UNIQUE.
      73              :     constexpr field_t& unique()         { flags |= flag_unique;         return *this; }
      74              : 
      75              :     /// Mark this column as indexed.
      76              :     constexpr field_t& indexed()        { flags |= flag_indexed;        return *this; }
      77              : 
      78              :     /// Return true if this column is a primary key.
      79            7 :     constexpr bool is_primary_key()    const { return flags & flag_primary_key; }
      80              : 
      81              :     /// Return true if this column is auto-incrementing.
      82            4 :     constexpr bool is_auto_increment() const { return flags & flag_auto_increment; }
      83              : 
      84              :     /// Return true if this column is NOT NULL.
      85            4 :     constexpr bool is_not_null()       const { return flags & flag_not_null; }
      86              : 
      87              :     /// Return true if this column is UNIQUE.
      88            3 :     constexpr bool is_unique()         const { return flags & flag_unique; }
      89              : 
      90              :     /// Return true if this column is indexed.
      91            2 :     constexpr bool is_indexed()        const { return flags & flag_indexed; }
      92              : 
      93              :     /// Return the member value from an object.
      94            5 :     constexpr T const& get(C const& obj) const
      95              :     {
      96            5 :         return obj.*pointer;
      97              :     }
      98              : 
      99              :     /// Set the member value on an object.
     100            1 :     constexpr void set(C& obj, T const& value) const
     101              :     {
     102            1 :         obj.*pointer = value;
     103            1 :     }
     104              : 
     105              :     /// Set the member value on an object by move.
     106            2 :     constexpr void set(C& obj, T&& value) const
     107              :     {
     108            2 :         obj.*pointer = static_cast<T&&>(value);
     109            2 :     }
     110              : };
     111              : 
     112              : /** Create a field descriptor for a member pointer.
     113              : 
     114              :     Deduces `T` and `C` from the member pointer so
     115              :     template arguments are never needed at the call site.
     116              : 
     117              :     @par Example
     118              :     @code
     119              :     field("email", &user::email)
     120              :     field("id", &user::id).primary_key().auto_increment()
     121              :     @endcode
     122              : 
     123              :     @param name Column name in the database table.
     124              :     @param ptr  Pointer to the mapped data member.
     125              : 
     126              :     @return A @ref field_t descriptor for the member.
     127              : 
     128              :     @see field_t
     129              : */
     130              : template <typename T, typename C>
     131              : constexpr auto field(std::string_view name, T C::* ptr)
     132              :     -> field_t<T, C>
     133              : {
     134              :     return { name, ptr };
     135              : }
     136              : 
     137              : /** Describe a nested struct flattened into the parent table.
     138              : 
     139              :     The nested type must provide its own `tag_invoke`
     140              :     overloads for @ref fields_t. A prefix is prepended
     141              :     to each nested column name to avoid collisions.
     142              : 
     143              :     @par Example
     144              :     @code
     145              :     // If user::addr is an address with field "street",
     146              :     // the resulting column is "addr_street".
     147              :     embed("addr_", &user::addr)
     148              :     @endcode
     149              : 
     150              :     @tparam T Nested struct type.
     151              :     @tparam C Containing class type.
     152              : 
     153              :     @see embed, field_t
     154              : */
     155              : template <typename T, typename C>
     156              : struct embed_t
     157              : {
     158              :     using value_type = T;
     159              :     using class_type = C;
     160              : 
     161              :     std::string_view prefix;
     162              :     T C::* pointer;
     163              : 
     164              :     /// Return the nested struct from an object.
     165            2 :     constexpr T const& get(C const& obj) const
     166              :     {
     167            2 :         return obj.*pointer;
     168              :     }
     169              : 
     170              :     /// Set the nested struct on an object.
     171            1 :     constexpr void set(C& obj, T const& value) const
     172              :     {
     173            1 :         obj.*pointer = value;
     174            1 :     }
     175              : 
     176              :     /// Set the nested struct on an object by move.
     177            1 :     constexpr void set(C& obj, T&& value) const
     178              :     {
     179            1 :         obj.*pointer = static_cast<T&&>(value);
     180            1 :     }
     181              : };
     182              : 
     183              : /** Create an embedded field descriptor for a nested struct.
     184              : 
     185              :     @param prefix String prepended to nested column names.
     186              :     @param ptr    Pointer to the nested data member.
     187              : 
     188              :     @return An @ref embed_t descriptor.
     189              : 
     190              :     @see embed_t
     191              : */
     192              : template <typename T, typename C>
     193              : constexpr auto embed(std::string_view prefix, T C::* ptr)
     194              :     -> embed_t<T, C>
     195              : {
     196              :     return { prefix, ptr };
     197              : }
     198              : 
     199              : /** Describe a one-to-one relationship to another table.
     200              : 
     201              :     The referenced type must provide its own
     202              :     `tag_invoke` overloads for @ref table_name_t
     203              :     and @ref fields_t.
     204              : 
     205              :     @par Example
     206              :     @code
     207              :     has_one("address_id", &user::addr)
     208              :     @endcode
     209              : 
     210              :     @tparam T Referenced struct type.
     211              :     @tparam C Containing class type.
     212              : 
     213              :     @see has_one, has_many_t
     214              : */
     215              : template <typename T, typename C>
     216              : struct has_one_t
     217              : {
     218              :     using value_type = T;
     219              :     using class_type = C;
     220              : 
     221              :     std::string_view foreign_key;
     222              :     T C::* pointer;
     223              : 
     224              :     /// Return the related object from the parent.
     225            1 :     constexpr T const& get(C const& obj) const
     226              :     {
     227            1 :         return obj.*pointer;
     228              :     }
     229              : 
     230              :     /// Set the related object on the parent.
     231            1 :     constexpr void set(C& obj, T const& value) const
     232              :     {
     233            1 :         obj.*pointer = value;
     234            1 :     }
     235              : 
     236              :     /// Set the related object on the parent by move.
     237            1 :     constexpr void set(C& obj, T&& value) const
     238              :     {
     239            1 :         obj.*pointer = static_cast<T&&>(value);
     240            1 :     }
     241              : };
     242              : 
     243              : /** Create a one-to-one relationship descriptor.
     244              : 
     245              :     @param foreign_key Column name of the foreign key.
     246              :     @param ptr         Pointer to the related data member.
     247              : 
     248              :     @return A @ref has_one_t descriptor.
     249              : 
     250              :     @see has_one_t
     251              : */
     252              : template <typename T, typename C>
     253              : constexpr auto has_one(std::string_view foreign_key, T C::* ptr)
     254              :     -> has_one_t<T, C>
     255              : {
     256              :     return { foreign_key, ptr };
     257              : }
     258              : 
     259              : /** Describe a one-to-many relationship.
     260              : 
     261              :     The child type has a member acting as the foreign
     262              :     key pointing back to the parent's primary key.
     263              :     The child's foreign key member pointer is stored
     264              :     so the library can build the appropriate JOIN or
     265              :     subquery.
     266              : 
     267              :     @par Example
     268              :     @code
     269              :     has_many(&user::posts, &post::user_id)
     270              :     @endcode
     271              : 
     272              :     @tparam Collection Container type in the parent
     273              :         (e.g. `std::vector< post >`).
     274              :     @tparam Parent     Parent class type.
     275              :     @tparam FK         Foreign key value type in the child.
     276              :     @tparam Child      Child class type.
     277              : 
     278              :     @see has_many, has_one_t
     279              : */
     280              : template <typename Collection, typename Parent, typename FK, typename Child>
     281              : struct has_many_t
     282              : {
     283              :     using collection_type = Collection;
     284              :     using parent_type     = Parent;
     285              :     using child_type      = Child;
     286              :     using fk_value_type   = FK;
     287              : 
     288              :     Collection Parent::* pointer;
     289              :     FK Child::* foreign_key;
     290              : };
     291              : 
     292              : /** Create a one-to-many relationship descriptor.
     293              : 
     294              :     @param ptr Pointer to the collection member in the parent.
     295              :     @param fk  Pointer to the foreign key member in the child.
     296              : 
     297              :     @return A @ref has_many_t descriptor.
     298              : 
     299              :     @see has_many_t
     300              : */
     301              : template <
     302              :     typename Collection, typename Parent,
     303              :     typename FK, typename Child>
     304              : constexpr auto has_many(
     305              :     Collection Parent::* ptr,
     306              :     FK Child::* fk)
     307              :     -> has_many_t<Collection, Parent, FK, Child>
     308              : {
     309              :     return { ptr, fk };
     310              : }
     311              : 
     312              : /** Tag type for retrieving the table name of a mapped type.
     313              : 
     314              :     Customize via `tag_invoke`:
     315              : 
     316              :     @par Example
     317              :     @code
     318              :     constexpr auto tag_invoke(
     319              :         db::table_name_t, user const&)
     320              :     {
     321              :         return "users";
     322              :     }
     323              :     @endcode
     324              : 
     325              :     @see fields_t, HasMapping
     326              : */
     327              : struct table_name_t
     328              : {
     329              :     template <typename T>
     330            3 :     constexpr auto operator()(T const& v) const
     331              :     {
     332            3 :         return tag_invoke(*this, v);
     333              :     }
     334              : };
     335              : 
     336              : /** Tag type for retrieving the field descriptors of a mapped type.
     337              : 
     338              :     Customize via `tag_invoke`:
     339              : 
     340              :     @par Example
     341              :     @code
     342              :     constexpr auto tag_invoke(
     343              :         db::fields_t, user const&)
     344              :     {
     345              :         return std::tuple(
     346              :             db::field("id", &user::id)
     347              :                 .primary_key().auto_increment(),
     348              :             db::field("email", &user::email)
     349              :                 .not_null().unique(),
     350              :             db::field("name", &user::name));
     351              :     }
     352              :     @endcode
     353              : 
     354              :     @see table_name_t, HasMapping
     355              : */
     356              : struct fields_t
     357              : {
     358              :     template <typename T>
     359              :     constexpr auto operator()(T const& v) const
     360              :     {
     361              :         return tag_invoke(*this, v);
     362              :     }
     363              : };
     364              : 
     365              : /// Customization point object for @ref table_name_t.
     366              : inline constexpr table_name_t table_name{};
     367              : 
     368              : /// Customization point object for @ref fields_t.
     369              : inline constexpr fields_t     fields{};
     370              : 
     371              : /** Concept for types with a complete schema mapping.
     372              : 
     373              :     A conforming type must provide `tag_invoke`
     374              :     overloads for both @ref table_name_t and
     375              :     @ref fields_t.
     376              : 
     377              :     @par Syntactic Requirements
     378              :     @li `tag_invoke( table_name, v )` is convertible
     379              :         to `std::string_view`.
     380              :     @li `tag_invoke( fields, v )` is a valid expression.
     381              : 
     382              :     @tparam T The type to check for a schema mapping.
     383              : 
     384              :     @see table_name_t, fields_t
     385              : */
     386              : template <typename T>
     387              : concept HasMapping = requires(T const& v)
     388              : {
     389              :     { tag_invoke(table_name, v) } -> std::convertible_to<std::string_view>;
     390              :     { tag_invoke(fields,     v) };
     391              : };
     392              : 
     393              : namespace detail {
     394              : 
     395              : template <typename Tuple, typename F, std::size_t... Is>
     396            3 : constexpr void for_each_impl(
     397              :     Tuple const& t, F&& f, std::index_sequence<Is...>)
     398              : {
     399            3 :     (f(std::get<Is>(t)), ...);
     400            3 : }
     401              : 
     402              : } // namespace detail
     403              : 
     404              : /** Invoke a callable for each field in a mapped type.
     405              : 
     406              :     @param f Callable invoked as `f( field_descriptor )`
     407              :         for every field in `T`'s mapping.
     408              : 
     409              :     @tparam T A type satisfying @ref HasMapping.
     410              : 
     411              :     @see field_count
     412              : */
     413              : template <HasMapping T, typename F>
     414            3 : constexpr void for_each_field(F&& f)
     415              : {
     416            3 :     constexpr auto fs = tag_invoke(fields, T{});
     417            3 :     detail::for_each_impl(
     418              :         fs,
     419              :         static_cast<F&&>(f),
     420              :         std::make_index_sequence<
     421              :             std::tuple_size_v<decltype(fs)>>{});
     422            3 : }
     423              : 
     424              : /** Return the number of fields in a mapped type.
     425              : 
     426              :     @tparam T A type satisfying @ref HasMapping.
     427              : 
     428              :     @return The compile-time field count.
     429              : 
     430              :     @see for_each_field
     431              : */
     432              : template <HasMapping T>
     433              : constexpr std::size_t field_count()
     434              : {
     435              :     constexpr auto fs = tag_invoke(fields, T{});
     436              :     return std::tuple_size_v<decltype(fs)>;
     437              : }
     438              : 
     439              : } // namespace db
     440              : } // namespace http
     441              : } // namespace boost
     442              : 
     443              : #endif
        

Generated by: LCOV version 2.3