ores.cpp.presentation.history_field_mapper_impl
Implements render_{entity}_fields(): one field_value push per
field, dispatched on a set of render_is_* flags computed directly
from each column's raw cpp_type string — deliberately not the
is_uuid=/=is_nullable_string=/=is_already_optional=/=is_plain_string
flags the repository/SQL facets use, since those encode a nullable-to-
optional promotion for the storage layer that the domain class
template (cpp_domain_type_class.hpp.mustache) does not apply: it
emits \{\{\{cpp_type\}\}\} verbatim, so a column with
:nullable: true but an explicit :cpp_type: std::string override
gets a plain std::string field, not std::optional<std::string>.
The mapper must match that same ground truth, or it doesn't compile
against the actual generated struct. presentation profile.
Scope: both flat and C1202-nested (has_identity_group=/
=has_audit_group) entities, matching the mapper/repository/service
facets' existing branching convention (v.identity.x=/=v.audit.x vs
flat v.x). Primary key supports string and uuid only; natural
keys support string, uuid, date, timestamp, and int. Natural
keys are not identity-group-aware (mirroring the mapper facet's same
carve-out — an entity with natural keys inside the identity group
would need this extended). Enum columns render via rfl::enum_to_string
(render_is_enum, derived from the raw is_enum flag rather than the
nullable-narrowed is_enum column flag, matching every other
render_is_* flag's ground-truth-over-derived-flag rule above) –
added for the
ores.synthetic.market_data_generation_config scope=/=binding_mode
columns (Synthetic data scope and binding story,
compass show 9DEED9D1-680C-4B87-A7DF-2B9AA9ED7277). Nullable enum
columns are not yet covered.
See the Template variable reference for the complete list of available variables and their semantics.
Template
The full template source. Edit here and re-tangle with
compass build --direct tangle_codegen_templates to regenerate
library/templates/cpp_history_field_mapper.cpp.mustache.
{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/cpp_presentation.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#include "ores.{{component_core}}/presentation/{{entity_singular}}_history_field_mapper.hpp"
{{#has_render_uuid_columns}}
#include <boost/uuid/uuid_io.hpp>
{{/has_render_uuid_columns}}
#include "ores.platform/time/datetime.hpp"
{{#has_date_natural_keys}}
#include <format>
{{/has_date_natural_keys}}
{{#has_enum_columns}}
#include <rfl/enums.hpp>
{{/has_enum_columns}}
namespace ores::{{component}}::presentation {
std::vector<ores::diff::domain::field_value>
render_{{entity_singular}}_fields(const domain::{{entity_singular}}& v) {
using ores::diff::domain::field_value;
std::vector<field_value> fields;
{{#has_identity_group}}
{{#primary_key.is_uuid}}
fields.push_back(
{.name = "{{primary_key.render_label}}", .value = boost::uuids::to_string(v.identity.{{primary_key.column}})});
{{/primary_key.is_uuid}}
{{^primary_key.is_uuid}}
fields.push_back({.name = "{{primary_key.render_label}}", .value = v.identity.{{primary_key.column}}});
{{/primary_key.is_uuid}}
{{/has_identity_group}}
{{^has_identity_group}}
{{#primary_key.is_uuid}}
fields.push_back(
{.name = "{{primary_key.render_label}}", .value = boost::uuids::to_string(v.{{primary_key.column}})});
{{/primary_key.is_uuid}}
{{^primary_key.is_uuid}}
fields.push_back({.name = "{{primary_key.render_label}}", .value = v.{{primary_key.column}}});
{{/primary_key.is_uuid}}
{{/has_identity_group}}
{{#natural_keys}}
{{#is_uuid}}
fields.push_back({.name = "{{render_label}}", .value = boost::uuids::to_string(v.{{column}})});
{{/is_uuid}}
{{^is_uuid}}
{{#is_date}}
fields.push_back({.name = "{{render_label}}", .value = std::format("{:%Y-%m-%d}", v.{{column}})});
{{/is_date}}
{{^is_date}}
{{#is_timestamp}}
fields.push_back(
{.name = "{{render_label}}", .value = ores::platform::time::datetime::to_iso8601_utc(v.{{column}})});
{{/is_timestamp}}
{{^is_timestamp}}
{{#is_int}}
fields.push_back({.name = "{{render_label}}", .value = std::to_string(v.{{column}})});
{{/is_int}}
{{^is_int}}
fields.push_back({.name = "{{render_label}}", .value = v.{{column}}});
{{/is_int}}
{{/is_timestamp}}
{{/is_date}}
{{/is_uuid}}
{{/natural_keys}}
{{#columns}}
{{#is_identity_group_column}}
{{#render_is_uuid}}
fields.push_back({.name = "{{render_label}}", .value = boost::uuids::to_string(v.identity.{{name}})});
{{/render_is_uuid}}
{{#render_is_optional_uuid}}
fields.push_back({.name = "{{render_label}}",
.value = v.identity.{{name}} ? boost::uuids::to_string(*v.identity.{{name}}) : std::string{}});
{{/render_is_optional_uuid}}
{{#render_is_timestamp}}
fields.push_back(
{.name = "{{render_label}}", .value = ores::platform::time::datetime::to_iso8601_utc(v.identity.{{name}})});
{{/render_is_timestamp}}
{{#render_is_optional_timestamp}}
fields.push_back({.name = "{{render_label}}",
.value = v.identity.{{name}} ? ores::platform::time::datetime::to_iso8601_utc(*v.identity.{{name}}) : std::string{}});
{{/render_is_optional_timestamp}}
{{#render_is_optional_string}}
fields.push_back({.name = "{{render_label}}", .value = v.identity.{{name}}.value_or(std::string{})});
{{/render_is_optional_string}}
{{#render_is_optional_bool}}
fields.push_back({.name = "{{render_label}}", .value = v.identity.{{name}} ? (*v.identity.{{name}} ? "true" : "false") : std::string{}});
{{/render_is_optional_bool}}
{{#render_is_optional_int}}
fields.push_back({.name = "{{render_label}}",
.value = v.identity.{{name}} ? std::to_string(*v.identity.{{name}}) : std::string{}});
{{/render_is_optional_int}}
{{#render_is_optional_double}}
fields.push_back({.name = "{{render_label}}",
.value = v.identity.{{name}} ? std::to_string(*v.identity.{{name}}) : std::string{}});
{{/render_is_optional_double}}
{{#render_is_string}}
fields.push_back({.name = "{{render_label}}", .value = v.identity.{{name}}});
{{/render_is_string}}
{{#render_is_bool}}
fields.push_back({.name = "{{render_label}}", .value = v.identity.{{name}} ? "true" : "false"});
{{/render_is_bool}}
{{#render_is_int}}
fields.push_back({.name = "{{render_label}}", .value = std::to_string(v.identity.{{name}})});
{{/render_is_int}}
{{#render_is_double}}
fields.push_back({.name = "{{render_label}}", .value = std::to_string(v.identity.{{name}})});
{{/render_is_double}}
{{#render_is_enum}}
fields.push_back({.name = "{{render_label}}", .value = rfl::enum_to_string(v.identity.{{name}})});
{{/render_is_enum}}
{{/is_identity_group_column}}
{{^is_identity_group_column}}
{{#render_is_uuid}}
fields.push_back({.name = "{{render_label}}", .value = boost::uuids::to_string(v.{{name}})});
{{/render_is_uuid}}
{{#render_is_optional_uuid}}
fields.push_back({.name = "{{render_label}}",
.value = v.{{name}} ? boost::uuids::to_string(*v.{{name}}) : std::string{}});
{{/render_is_optional_uuid}}
{{#render_is_timestamp}}
fields.push_back(
{.name = "{{render_label}}", .value = ores::platform::time::datetime::to_iso8601_utc(v.{{name}})});
{{/render_is_timestamp}}
{{#render_is_optional_timestamp}}
fields.push_back({.name = "{{render_label}}",
.value = v.{{name}} ? ores::platform::time::datetime::to_iso8601_utc(*v.{{name}}) : std::string{}});
{{/render_is_optional_timestamp}}
{{#render_is_optional_string}}
fields.push_back({.name = "{{render_label}}", .value = v.{{name}}.value_or(std::string{})});
{{/render_is_optional_string}}
{{#render_is_optional_bool}}
fields.push_back({.name = "{{render_label}}", .value = v.{{name}} ? (*v.{{name}} ? "true" : "false") : std::string{}});
{{/render_is_optional_bool}}
{{#render_is_optional_int}}
fields.push_back({.name = "{{render_label}}",
.value = v.{{name}} ? std::to_string(*v.{{name}}) : std::string{}});
{{/render_is_optional_int}}
{{#render_is_optional_double}}
fields.push_back({.name = "{{render_label}}",
.value = v.{{name}} ? std::to_string(*v.{{name}}) : std::string{}});
{{/render_is_optional_double}}
{{#render_is_string}}
fields.push_back({.name = "{{render_label}}", .value = v.{{name}}});
{{/render_is_string}}
{{#render_is_bool}}
fields.push_back({.name = "{{render_label}}", .value = v.{{name}} ? "true" : "false"});
{{/render_is_bool}}
{{#render_is_int}}
fields.push_back({.name = "{{render_label}}", .value = std::to_string(v.{{name}})});
{{/render_is_int}}
{{#render_is_double}}
fields.push_back({.name = "{{render_label}}", .value = std::to_string(v.{{name}})});
{{/render_is_double}}
{{#render_is_enum}}
fields.push_back({.name = "{{render_label}}", .value = rfl::enum_to_string(v.{{name}})});
{{/render_is_enum}}
{{/is_identity_group_column}}
{{/columns}}
{{#has_audit_group}}
{{#has_audit_columns}}
fields.push_back({.name = "Modified By", .value = v.audit.modified_by});
fields.push_back({.name = "Performed By", .value = v.audit.performed_by});
fields.push_back({.name = "Change Reason Code", .value = v.audit.change_reason_code});
fields.push_back({.name = "Change Commentary", .value = v.audit.change_commentary});
{{/has_audit_columns}}
fields.push_back(
{.name = "Recorded At", .value = ores::platform::time::datetime::to_iso8601_utc(v.audit.recorded_at)});
{{/has_audit_group}}
{{^has_audit_group}}
{{#has_audit_columns}}
fields.push_back({.name = "Modified By", .value = v.modified_by});
fields.push_back({.name = "Performed By", .value = v.performed_by});
fields.push_back({.name = "Change Reason Code", .value = v.change_reason_code});
fields.push_back({.name = "Change Commentary", .value = v.change_commentary});
{{/has_audit_columns}}
fields.push_back(
{.name = "Recorded At", .value = ores::platform::time::datetime::to_iso8601_utc(v.recorded_at)});
{{/has_audit_group}}
return fields;
}
}
{{/domain_entity}}