{# TODO(yzshen): Make these templates more readable. #}
{# Serializes the specified struct.
|struct| is the struct definition.
|struct_display_name| is the display name for the struct that can be showed
in error/log messages, for example, "FooStruct", "FooMethod request".
|input_field_pattern| should be a pattern that contains one string
placeholder, for example, "input->%s", "p_%s". The placeholder will be
substituted with struct field names to refer to the input fields.
|fragment| is the name of the MessageFragment to serialize into.
|input_may_be_temp|: please see the comments of get_serialized_size.
This macro is expanded to do serialization for both:
- user-defined structs: the input is an instance of the corresponding struct
wrapper class.
- method parameters/response parameters: the input is a list of
arguments. #}
{%- macro serialize(struct, struct_display_name, input_field_pattern, fragment,
input_may_be_temp=False) -%}
{{fragment}}.Allocate();
{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
{%- if pf|is_nullable_value_kind_packed_field %}
{%- if pf|is_primary_nullable_value_kind_packed_field %}
{%- set original_field = pf.original_field %}
{%- set has_value_pf = pf %}
{%- set value_pf = pf.linked_value_packed_field %}
{%- set input_field = input_field_pattern|format(original_field.name) %}
{%- set name = original_field.name %}
{%- set kind = original_field.kind %}
{%- set serializer_type = value_pf.field.kind|unmapped_type_for_serializer %}
{{fragment}}->{{has_value_pf.field.name}} = {{input_field}}.has_value();
{%- if kind|is_enum_kind %}
if ({{input_field}}.has_value()) {
mojo::internal::Serialize<{{serializer_type}}>(
{{input_field}}.value(), &{{fragment}}->{{value_pf.field.name}});
} else {
{{fragment}}->{{value_pf.field.name}} =
static_cast<int32_t>({{serializer_type}}::kMinValue);
}
{%- else %}
if ({{input_field}}.has_value()) {
{{fragment}}->{{value_pf.field.name}} = {{input_field}}.value();
}
{%- endif %}
{%- endif %}
{%- else %}
{%- set input_field = input_field_pattern|format(pf.field.name) %}
{%- set name = pf.field.name %}
{%- set kind = pf.field.kind %}
{%- set serializer_type = kind|unmapped_type_for_serializer %}
{%- if kind|is_object_kind or kind|is_any_handle_or_interface_kind %}
{%- set original_input_field = input_field_pattern|format(name) %}
{%- set input_field = "in_%s"|format(name) if input_may_be_temp
else original_input_field %}
{%- if input_may_be_temp %}
decltype({{original_input_field}}) in_{{name}} = {{original_input_field}};
{%- endif %}
{%- endif %}
{%- if kind|is_object_kind %}
{%- if kind|is_array_kind or kind|is_map_kind %}
mojo::internal::MessageFragment<
typename decltype({{fragment}}->{{name}})::BaseType>
{{name}}_fragment({{fragment}}.message());
constexpr const mojo::internal::ContainerValidateParams& {{name}}_validate_params =
{{kind|get_container_validate_params_ctor_args|indent(6)}};
mojo::internal::Serialize<{{serializer_type}}>(
{{input_field}}, {{name}}_fragment, &{{name}}_validate_params);
{{fragment}}->{{name}}.Set(
{{name}}_fragment.is_null() ? nullptr : {{name}}_fragment.data());
{%- elif kind|is_union_kind %}
mojo::internal::MessageFragment<decltype({{fragment}}->{{name}})>
{{name}}_fragment({{fragment}}.message());
{{name}}_fragment.Claim(&{{fragment}}->{{name}});
mojo::internal::Serialize<{{serializer_type}}>(
{{input_field}}, {{name}}_fragment, true);
{%- else %}
mojo::internal::MessageFragment<
typename decltype({{fragment}}->{{name}})::BaseType> {{name}}_fragment(
{{fragment}}.message());
mojo::internal::Serialize<{{serializer_type}}>(
{{input_field}}, {{name}}_fragment);
{{fragment}}->{{name}}.Set(
{{name}}_fragment.is_null() ? nullptr : {{name}}_fragment.data());
{%- endif %}
{%- if not kind|is_nullable_kind %}
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
{{fragment}}->{{name}}.is_null(),
mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
"null {{name}} in {{struct_display_name}}");
{%- endif %}
{%- elif kind|is_any_handle_or_interface_kind %}
mojo::internal::Serialize<{{serializer_type}}>(
{{input_field}}, &{{fragment}}->{{name}}, &{{fragment}}.message());
{%- if not kind|is_nullable_kind %}
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
!mojo::internal::IsHandleOrInterfaceValid({{fragment}}->{{name}}),
{%- if kind|is_associated_kind %}
mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
{%- else %}
mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
{%- endif %}
"invalid {{name}} in {{struct_display_name}}");
{%- endif %}
{%- elif kind|is_enum_kind %}
mojo::internal::Serialize<{{serializer_type}}>(
{{input_field}}, &{{fragment}}->{{name}});
{%- else %}
{{fragment}}->{{name}} = {{input_field}};
{%- endif %}
{%- endif %}
{%- endfor %}
{%- endmacro -%}
{# Deserializes the specified struct.
|struct| is the struct definition.
|input| is the name of the input struct data view. It is expected to be
non-null.
|output_field_pattern| should be a pattern that contains one string
placeholder, for example, "result->%s", "p_%s". The placeholder will be
substituted with struct field names to refer to the output fields.
|message| is the name of the Message object we're deserializing.
|success| is the name of a bool variable to track success of the operation.
This macro is expanded to do deserialization for both:
- user-defined structs: the output is an instance of the corresponding
struct wrapper class.
- method parameters/response parameters: the output is a list of
arguments. #}
{%- macro deserialize(struct, input, output_field_pattern, success) -%}
{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
{%- if pf|is_nullable_value_kind_packed_field %}
{%- if pf|is_primary_nullable_value_kind_packed_field %}
{%- set original_field = pf.original_field %}
{%- set output_field = output_field_pattern|format(original_field.name) %}
{%- set name = original_field.name %}
{%- set kind = original_field.kind %}
{%- if kind|is_enum_kind %}
if ({{success}} && !{{input}}.Read{{name|under_to_camel}}(&{{output_field}})) {
{{success}} = false;
{%- else %}
if ({{success}}) {
{{output_field}} = {{input}}.{{name}}();
{%- endif %}
}
{%- endif %}
{%- else %}
{%- set output_field = output_field_pattern|format(pf.field.name) %}
{%- set name = pf.field.name %}
{%- set kind = pf.field.kind %}
{%- if kind|is_object_kind or kind|is_enum_kind %}
if ({{success}} && !{{input}}.Read{{name|under_to_camel}}(&{{output_field}}))
{{success}} = false;
{%- elif kind|is_any_handle_kind %}
if ({{success}})
{{output_field}} = {{input}}.Take{{name|under_to_camel}}();
{%- elif kind|is_any_interface_kind %}
if ({{success}}) {
{{output_field}} =
{{input}}.Take{{name|under_to_camel}}<decltype({{output_field}})>();
}
{%- else %}
if ({{success}})
{{output_field}} = {{input}}.{{name}}();
{%- endif %}
{%- endif %}
{%- endfor %}
{%- endmacro %}
{%- macro assert_nullable_output_type_if_necessary(kind, name) -%}
{%- if kind|is_nullable_kind and not kind|is_native_only_kind %}
static_assert(
mojo::internal::IsValidUserTypeForOptionalValue<
{{kind|unmapped_type_for_serializer}}, UserType>(),
"Attempting to read the optional `{{name}}` field into a type which "
"cannot represent a null value. Either wrap the destination object "
"with std::optional, ensure that any corresponding "
"{Struct/Union/Array/String}Traits define the necessary IsNull and "
"SetToNull methods, or use `MaybeRead{{name|under_to_camel}}` instead "
"of `Read{{name|under_to_camel}} if you're fine with null values being "
"silently ignored in this case.");
{%- endif %}
{%- endmacro %}