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-06-13 19:44:58 Functions: 100.0 % 28 28

           TLA  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 HIT           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