ores.cpp.repository.mapper_impl
Table of Contents
Domain ↔ entity conversions including timestamp handling. repository profile.
See the Template variable reference for the complete list of available variables and their semantics.
1. Template
The full template source. Edit here and re-tangle with
compass build --direct tangle_codegen_templates to regenerate
library/templates/cpp_domain_type_mapper.cpp.mustache.
{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/cpp_repository.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#include "ores.{{component_core}}/repository/{{entity_singular}}_mapper.hpp"
#include "ores.{{component_include}}/domain/{{entity_singular}}.hpp"
#include "ores.{{component_core}}/repository/{{entity_singular}}_entity.hpp"
{{#has_uuid_columns}}
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
{{/has_uuid_columns}}
{{#mapper_uses_datetime}}
#include "ores.platform/time/datetime.hpp"
{{/mapper_uses_datetime}}
{{#has_date_or_timestamp_natural_keys}}
#include <chrono>
{{/has_date_or_timestamp_natural_keys}}
{{#has_date_natural_keys}}
#include <format>
#include <sstream>
{{/has_date_natural_keys}}
{{#has_plain_date_columns}}
#include <chrono>
{{/has_plain_date_columns}}
{{#has_decimal_columns}}
#include "ores.utility/decimal/decimal.hpp"
{{/has_decimal_columns}}
{{#has_enum_columns}}
#include "ores.utility/codec/text_form.hpp"
{{/has_enum_columns}}
{{#mapper_enum_includes}}
#include "{{path}}"
{{/mapper_enum_includes}}
{{#has_inet_columns}}
#include <boost/asio/ip/address.hpp>
{{/has_inet_columns}}
{{#has_base64_columns}}
#include "ores.utility/convert/base64_converter.hpp"
{{/has_base64_columns}}
#include "ores.database/repository/mapper_helpers.hpp"
#include "ores.logging/boost_severity.hpp"
#include <boost/log/sources/severity_feature.hpp>
#include <vector>
{{#mapper_uses_optional}}
#include <optional>
{{/mapper_uses_optional}}
{{#mapper_uses_string}}
#include <string>
{{/mapper_uses_string}}
{{#mapper_uses_string_view}}
#include <string_view>
{{/mapper_uses_string_view}}
#include "ores.{{component_include}}/domain/{{entity_singular}}_json_io.hpp" // IWYU pragma: keep.
namespace ores::{{component}}::repository {
using namespace ores::logging;
{{^component_is_database}}
using namespace ores::database::repository;
{{/component_is_database}}
domain::{{entity_singular}}
{{entity_singular}}_mapper::map(const {{entity_singular}}_entity& v) {
BOOST_LOG_SEV(lg(), trace) << "Mapping db entity: " << v;
domain::{{entity_singular}} r;
{{#has_audit_columns}}
r.{{{scaffold_prefix}}}version = v.version;
{{/has_audit_columns}}
{{#has_tenant_id}}
{{#nullable_tenant_id}}
if (v.tenant_id)
r.{{{scaffold_prefix}}}tenant_id = *v.tenant_id;
{{/nullable_tenant_id}}
{{^nullable_tenant_id}}
r.{{{scaffold_prefix}}}tenant_id = v.tenant_id;
{{/nullable_tenant_id}}
{{/has_tenant_id}}
{{#primary_key.columns}}
r.{{{group_prefix}}}{{column}} = v.{{column}};
{{/primary_key.columns}}
{{! Natural keys are not identity-group-column-aware: they always map flat.
An entity with both has_identity_group and non-empty natural_keys would
produce incorrect code if those keys live in the identity sub-struct. }}
{{#natural_keys}}
{{#is_optional_uuid}}
{{#render_is_uuid}}
r.{{{group_prefix}}}{{column}} = v.{{column}}.has_value() ? *v.{{column}} : boost::uuids::uuid{};
{{/render_is_uuid}}
{{^render_is_uuid}}
r.{{{group_prefix}}}{{column}} = v.{{column}};
{{/render_is_uuid}}
{{/is_optional_uuid}}
{{^is_optional_uuid}}
r.{{{group_prefix}}}{{column}} = v.{{column}};
{{/is_optional_uuid}}
{{/natural_keys}}
{{#columns}}
{{^sql_only}}
{{#is_uuid}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_uuid}}
{{#is_optional_uuid}}
{{#render_is_uuid}}
r.{{{group_prefix}}}{{name}} = v.{{name}}.has_value() ? *v.{{name}} : boost::uuids::uuid{};
{{/render_is_uuid}}
{{^render_is_uuid}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/render_is_uuid}}
{{/is_optional_uuid}}
{{#is_optional_timestamp}}
{{#render_is_timestamp}}
r.{{{group_prefix}}}{{name}} = v.{{name}}.value_or(db_timestamp{});
{{/render_is_timestamp}}
{{^render_is_timestamp}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/render_is_timestamp}}
{{/is_optional_timestamp}}
{{#is_required_timestamp}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_required_timestamp}}
{{#is_optional_date}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_optional_date}}
{{#is_required_date}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_required_date}}
{{#is_optional_decimal}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_optional_decimal}}
{{#is_required_decimal}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_required_decimal}}
{{#is_required_inet}}
// PostgreSQL renders an inet with its netmask, and the domain holds an
// address, which has none: the mask is the part this type cannot carry.
r.{{{group_prefix}}}{{name}} = boost::asio::ip::make_address(
v.{{name}}.substr(0, v.{{name}}.find('/')));
{{/is_required_inet}}
{{#is_base64}}
r.{{{group_prefix}}}{{name}} = utility::convert::base64_converter::convert(v.{{name}});
{{/is_base64}}
{{^render_is_enum}}
{{#is_nullable_string}}
{{#render_is_optional_string}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/render_is_optional_string}}
{{^render_is_optional_string}}
r.{{{group_prefix}}}{{name}} = v.{{name}}.value_or("");
{{/render_is_optional_string}}
{{/is_nullable_string}}
{{/render_is_enum}}
{{#is_nullable_numeric}}
r.{{{group_prefix}}}{{name}} = v.{{name}}.value_or(0);
{{/is_nullable_numeric}}
{{#is_already_optional}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_already_optional}}
{{#is_enum}}
r.{{{group_prefix}}}{{name}} =
ores::utility::codec::text_form<{{{cpp_type}}}>::from_text(v.{{name}});
{{/is_enum}}
{{#is_value_type}}
r.{{{group_prefix}}}{{name}} = {{{cpp_type}}}::from_string(v.{{name}}).value();
{{/is_value_type}}
{{#render_is_enum}}
{{^is_enum}}
r.{{{group_prefix}}}{{name}} = v.{{name}}.has_value() ?
ores::utility::codec::text_form<{{{render_cpp_type}}}>::from_text(*v.{{name}}) :
{{{render_cpp_type}}}{};
{{/is_enum}}
{{/render_is_enum}}
{{#is_simple}}
r.{{{group_prefix}}}{{name}} = v.{{name}};
{{/is_simple}}
{{/sql_only}}
{{/columns}}
{{#has_audit_columns}}
r.{{{audit_prefix}}}modified_by = v.modified_by;
r.{{{audit_prefix}}}performed_by = v.performed_by;
r.{{{audit_prefix}}}change_reason_code = v.change_reason_code;
r.{{{audit_prefix}}}change_commentary = v.change_commentary;
r.{{{audit_prefix}}}recorded_at = v.valid_from;
{{/has_audit_columns}}
BOOST_LOG_SEV(lg(), trace) << "Mapped db entity. Result: " << r;
return r;
}
{{entity_singular}}_entity
{{entity_singular}}_mapper::map(const domain::{{entity_singular}}& v) {
BOOST_LOG_SEV(lg(), trace) << "Mapping domain entity: " << v;
{{entity_singular}}_entity r;
{{#primary_key.columns}}
r.{{column}} = v.{{{group_prefix}}}{{column}};
{{/primary_key.columns}}
{{#has_tenant_id}}
{{#nullable_tenant_id}}
r.tenant_id = v.{{{scaffold_prefix}}}tenant_id;
{{/nullable_tenant_id}}
{{^nullable_tenant_id}}
r.tenant_id = v.{{{scaffold_prefix}}}tenant_id;
{{/nullable_tenant_id}}
{{/has_tenant_id}}
{{#has_audit_columns}}
r.version = v.{{{scaffold_prefix}}}version;
{{/has_audit_columns}}
{{! Natural keys are not identity-group-column-aware: they always map flat.
An entity with both has_identity_group and non-empty natural_keys would
produce incorrect code if those keys live in the identity sub-struct. }}
{{#natural_keys}}
{{#is_optional_uuid}}
{{#render_is_optional_uuid}}
r.{{column}} = v.{{{group_prefix}}}{{column}};
{{/render_is_optional_uuid}}
{{^render_is_optional_uuid}}
r.{{column}} = v.{{{group_prefix}}}{{column}}.value_or(boost::uuids::uuid{});
{{/render_is_optional_uuid}}
{{/is_optional_uuid}}
{{^is_optional_uuid}}
r.{{column}} = v.{{{group_prefix}}}{{column}};
{{/is_optional_uuid}}
{{/natural_keys}}
{{#columns}}
{{^sql_only}}
{{#is_uuid}}
{{#render_is_optional_uuid}}
r.{{name}} = v.{{{group_prefix}}}{{name}}.value_or(boost::uuids::uuid{});
{{/render_is_optional_uuid}}
{{^render_is_optional_uuid}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/render_is_optional_uuid}}
{{/is_uuid}}
{{#is_optional_uuid}}
{{#render_is_uuid}}
r.{{name}} = v.{{{group_prefix}}}{{name}} == boost::uuids::uuid{} ?
std::nullopt :
std::optional(v.{{{group_prefix}}}{{name}});
{{/render_is_uuid}}
{{^render_is_uuid}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/render_is_uuid}}
{{/is_optional_uuid}}
{{#is_optional_timestamp}}
{{#render_is_timestamp}}
r.{{name}} = v.{{{group_prefix}}}{{name}} == db_timestamp{} ?
std::nullopt :
std::optional(v.{{{group_prefix}}}{{name}});
{{/render_is_timestamp}}
{{^render_is_timestamp}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/render_is_timestamp}}
{{/is_optional_timestamp}}
{{#is_required_timestamp}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/is_required_timestamp}}
{{#is_optional_date}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/is_optional_date}}
{{#is_required_date}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/is_required_date}}
{{#is_optional_decimal}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/is_optional_decimal}}
{{#is_required_decimal}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/is_required_decimal}}
{{#is_required_inet}}
r.{{name}} = v.{{{group_prefix}}}{{name}}.to_string();
{{/is_required_inet}}
{{#is_base64}}
r.{{name}} = utility::convert::base64_converter::convert(v.{{{group_prefix}}}{{name}});
{{/is_base64}}
{{^render_is_enum}}
{{#is_nullable_string}}
{{#render_is_optional_string}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/render_is_optional_string}}
{{^render_is_optional_string}}
r.{{name}} = v.{{{group_prefix}}}{{name}}.empty() ? std::nullopt : std::optional(v.{{{group_prefix}}}{{name}});
{{/render_is_optional_string}}
{{/is_nullable_string}}
{{/render_is_enum}}
{{#is_nullable_numeric}}
r.{{name}} = v.{{{group_prefix}}}{{name}} == 0 ? std::nullopt : std::optional(v.{{{group_prefix}}}{{name}});
{{/is_nullable_numeric}}
{{#is_already_optional}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/is_already_optional}}
{{#is_enum}}
r.{{name}} = ores::utility::codec::text_form<decltype(v.{{{group_prefix}}}{{name}})>
::to_text(v.{{{group_prefix}}}{{name}});
{{/is_enum}}
{{#is_value_type}}
r.{{name}} = v.{{{group_prefix}}}{{name}}.to_string();
{{/is_value_type}}
{{#render_is_enum}}
{{^is_enum}}
r.{{name}} = v.{{{group_prefix}}}{{name}} == {{{render_cpp_type}}}{} ?
std::nullopt :
std::optional(ores::utility::codec::text_form<decltype(v.{{{group_prefix}}}{{name}})>
::to_text(v.{{{group_prefix}}}{{name}}));
{{/is_enum}}
{{/render_is_enum}}
{{#is_simple}}
{{#no_wire}}
r.{{name}} = v.{{{group_prefix}}}{{name}}.value();
{{/no_wire}}
{{^no_wire}}
r.{{name}} = v.{{{group_prefix}}}{{name}};
{{/no_wire}}
{{/is_simple}}
{{/sql_only}}
{{/columns}}
{{#has_audit_columns}}
r.modified_by = v.{{{audit_prefix}}}modified_by;
r.performed_by = v.{{{audit_prefix}}}performed_by;
r.change_reason_code = v.{{{audit_prefix}}}change_reason_code;
r.change_commentary = v.{{{audit_prefix}}}change_commentary;
{{/has_audit_columns}}
BOOST_LOG_SEV(lg(), trace) << "Mapped domain entity. Result: " << r;
return r;
}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_mapper::map(const std::vector<{{entity_singular}}_entity>& v) {
return map_vector<{{entity_singular}}_entity, domain::{{entity_singular}}>(
v,
[](const auto& ve) { return map(ve); },
lg(),
"db entities");
}
std::vector<{{entity_singular}}_entity>
{{entity_singular}}_mapper::map(const std::vector<domain::{{entity_singular}}>& v) {
return map_vector<domain::{{entity_singular}}, {{entity_singular}}_entity>(
v,
[](const auto& ve) { return map(ve); },
lg(),
"domain entities");
}
}
{{/domain_entity}}
{{#junction}}
#include "ores.{{component_core}}/repository/{{name_singular}}_mapper.hpp"
#include "ores.{{component_include}}/domain/{{name_singular}}.hpp"
#include "ores.{{component_core}}/repository/{{name_singular}}_entity.hpp"
{{#has_uuid_left_or_right}}
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
{{/has_uuid_left_or_right}}
{{#mapper_uses_datetime}}
#include "ores.platform/time/datetime.hpp"
{{/mapper_uses_datetime}}
#include "ores.database/repository/mapper_helpers.hpp"
#include "ores.logging/boost_severity.hpp"
#include <boost/log/sources/severity_feature.hpp>
#include <vector>
{{#mapper_uses_optional}}
#include <optional>
{{/mapper_uses_optional}}
{{#mapper_uses_string}}
#include <string>
{{/mapper_uses_string}}
{{#mapper_uses_string_view}}
#include <string_view>
{{/mapper_uses_string_view}}
#include "ores.{{component_include}}/domain/{{name_singular}}_json_io.hpp" // IWYU pragma: keep.
namespace ores::{{component}}::repository {
using namespace ores::logging;
{{^component_is_database}}
using namespace ores::database::repository;
{{/component_is_database}}
domain::{{name_singular}}
{{name_singular}}_mapper::map(const {{name_singular}}_entity& v) {
BOOST_LOG_SEV(lg(), trace) << "Mapping db entity: " << v;
domain::{{name_singular}} r;
r.version = v.version;
{{#has_tenant_id}}
r.tenant_id = v.tenant_id;
{{/has_tenant_id}}
r.{{left.column}} = v.{{left.column}};
r.{{right.column}} = v.{{right.column}};
{{#columns}}
{{#is_base64}}
r.{{name}} = utility::convert::base64_converter::convert(v.{{name}});
{{/is_base64}}
{{^is_base64}}
r.{{name}} = v.{{name}};
{{/is_base64}}
{{/columns}}
r.modified_by = v.modified_by;
r.performed_by = v.performed_by;
r.change_reason_code = v.change_reason_code;
r.change_commentary = v.change_commentary;
r.recorded_at = v.valid_from;
BOOST_LOG_SEV(lg(), trace) << "Mapped db entity. Result: " << r;
return r;
}
{{name_singular}}_entity
{{name_singular}}_mapper::map(const domain::{{name_singular}}& v) {
BOOST_LOG_SEV(lg(), trace) << "Mapping domain entity: " << v;
{{name_singular}}_entity r;
r.{{left.column}} = v.{{left.column}};
{{#has_tenant_id}}
r.tenant_id = v.tenant_id;
{{/has_tenant_id}}
r.{{right.column}} = v.{{right.column}};
r.version = v.version;
{{#columns}}
{{#is_base64}}
r.{{name}} = utility::convert::base64_converter::convert(v.{{name}});
{{/is_base64}}
{{^is_base64}}
r.{{name}} = v.{{name}};
{{/is_base64}}
{{/columns}}
r.modified_by = v.modified_by;
r.performed_by = v.performed_by;
r.change_reason_code = v.change_reason_code;
r.change_commentary = v.change_commentary;
BOOST_LOG_SEV(lg(), trace) << "Mapped domain entity. Result: " << r;
return r;
}
std::vector<domain::{{name_singular}}>
{{name_singular}}_mapper::map(const std::vector<{{name_singular}}_entity>& v) {
return map_vector<{{name_singular}}_entity, domain::{{name_singular}}>(
v,
[](const auto& ve) { return map(ve); },
lg(),
"db entities");
}
std::vector<{{name_singular}}_entity>
{{name_singular}}_mapper::map(const std::vector<domain::{{name_singular}}>& v) {
return map_vector<domain::{{name_singular}}, {{name_singular}}_entity>(
v,
[](const auto& ve) { return map(ve); },
lg(),
"domain entities");
}
}
{{/junction}}
2. See also
- Parent facet: ores.cpp.repository
- Template variable reference