chromium/third_party/inspector_protocol/templates/TypeBuilder_cpp.template

// This file is generated by TypeBuilder_cpp.template.

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include {{format_domain_include(config.protocol.package, domain.domain)}}

#include {{format_include(config.protocol.package, "Protocol")}}

#include "{{config.crdtp.dir}}/cbor.h"
#include "{{config.crdtp.dir}}/find_by_first.h"
#include "{{config.crdtp.dir}}/span.h"

{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
namespace {{domain.domain}} {

using {{config.crdtp.namespace}}::DeserializerState;
using {{config.crdtp.namespace}}::ProtocolTypeTraits;

// ------------- Enum values from types.

const char Metainfo::domainName[] = "{{domain.domain}}";
const char Metainfo::commandPrefix[] = "{{domain.domain}}.";
const char Metainfo::version[] = "{{domain.version}}";
  {% for type in domain.types %}
    {% if not protocol.generate_type(domain.domain, type.id) %}{% continue %} {% endif %}
    {% if "enum" in type %}

namespace {{type.id}}Enum {
      {% for literal in type.enum %}
const char {{ literal | dash_to_camelcase}}[] = "{{literal}}";
      {% endfor %}
} // namespace {{type.id}}Enum
      {% if protocol.is_exported(domain.domain, type.id) %}

namespace API {
namespace {{type.id}}Enum {
        {% for literal in type.enum %}
const char* {{ literal | dash_to_camelcase}} = "{{literal}}";
        {% endfor %}
} // namespace {{type.id}}Enum
} // namespace API
      {% endif %}
    {% endif %}

    {% for property in type.properties %}
      {% if "enum" in property %}

        {% for literal in property.enum %}
const char* {{type.id}}::{{property.name | to_title_case}}Enum::{{literal | dash_to_camelcase}} = "{{literal}}";
        {% endfor %}
      {% endif %}
    {% endfor %}
    {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
CRDTP_BEGIN_DESERIALIZER({{type.id}})
      {% for property in type.properties | sort(attribute = 'name', case_sensitive=True) %}
      {% if property.optional %}
    CRDTP_DESERIALIZE_FIELD_OPT("{{property.name}}", m_{{property.name}}),
      {% else %}
    CRDTP_DESERIALIZE_FIELD("{{property.name}}", m_{{property.name}}),
      {% endif %}
      {% endfor %}
CRDTP_END_DESERIALIZER()

CRDTP_BEGIN_SERIALIZER({{type.id}})
      {% for property in type.properties %}
    CRDTP_SERIALIZE_FIELD("{{property.name}}", m_{{property.name}});
      {% endfor %}
CRDTP_END_SERIALIZER();

    {% if protocol.is_exported(domain.domain, type.id) %}
// static
std::unique_ptr<API::{{type.id}}> API::{{type.id}}::fromBinary(const uint8_t* data, size_t length)
{
    return protocol::{{domain.domain}}::{{type.id}}::FromBinary(data, length);
}
    {% endif %}
  {% endfor %}

// ------------- Enum values from params.

  {% for command in join_arrays(domain, ["commands", "events"]) %}
    {% for param in join_arrays(command, ["parameters", "returns"]) %}
      {% if "enum" in param %}

namespace {{command.name | to_title_case}} {
namespace {{param.name | to_title_case}}Enum {
        {% for literal in param.enum %}
const char* {{ literal | to_title_case}} = "{{literal}}";
        {% endfor %}
} // namespace {{param.name | to_title_case}}Enum
} // namespace {{command.name | to_title_case }}
        {% if protocol.is_exported(domain.domain, command.name + "." + param.name) %}

namespace API {
namespace {{command.name | to_title_case}} {
namespace {{param.name | to_title_case}}Enum {
        {% for literal in param.enum %}
const char* {{ literal | to_title_case}} = "{{literal}}";
        {% endfor %}
} // namespace {{param.name | to_title_case}}Enum
} // namespace {{command.name | to_title_case }}
} // namespace API
        {% endif %}
      {% endif %}
    {% endfor %}
  {% endfor %}

// ------------- Frontend notifications.
  {% for event in domain.events %}
    {% if not protocol.generate_event(domain.domain, event.name) %}{% continue %}{% endif %}

void Frontend::{{event.name | to_method_case}}(
    {%- for parameter in event.parameters %}
      {% if "optional" in parameter -%}
        Maybe<{{protocol.resolve_type(parameter).raw_type}}>
      {%- else -%}
        {{protocol.resolve_type(parameter).pass_type}}
      {%- endif %} {{parameter.name}}{%- if not loop.last -%}, {% endif -%}
    {% endfor -%})
{
    if (!frontend_channel_)
        return;
      {% if event.parameters %}
    {{config.crdtp.namespace}}::ObjectSerializer serializer;
        {% for parameter in event.parameters %}
    serializer.AddField({{config.crdtp.namespace}}::MakeSpan("{{parameter.name}}"), {{parameter.name}});
        {% endfor %}
    frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}", serializer.Finish()));
      {% else %}
    frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}"));
      {% endif %}
}
  {% endfor %}

void Frontend::flush()
{
    frontend_channel_->FlushProtocolNotifications();
}

void Frontend::sendRawNotification(std::unique_ptr<Serializable> notification)
{
    frontend_channel_->SendProtocolNotification(std::move(notification));
}

// --------------------- Dispatcher.

class DomainDispatcherImpl : public protocol::DomainDispatcher {
public:
    DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend)
        : DomainDispatcher(frontendChannel)
        , m_backend(backend) {}
    ~DomainDispatcherImpl() override { }

    using CallHandler = void (DomainDispatcherImpl::*)(const {{config.crdtp.namespace}}::Dispatchable& dispatchable);

    std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) override;

  {% for command in domain.commands %}
    {% if "redirect" in command %}{% continue %}{% endif %}
    {% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
    void {{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable);
  {% endfor %}
 protected:
    Backend* m_backend;
};

namespace {
// This helper method with a static map of command methods (instance methods
// of DomainDispatcherImpl declared just above) by their name is used immediately below,
// in the DomainDispatcherImpl::Dispatch method.
DomainDispatcherImpl::CallHandler CommandByName({{config.crdtp.namespace}}::span<uint8_t> command_name) {
  static auto* commands = [](){
    auto* commands = new std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>,
                              DomainDispatcherImpl::CallHandler>>{
      {% for command in domain.commands|sort(attribute="name",case_sensitive=True) %}
        {% if "redirect" in command %}{% continue %}{% endif %}
        {% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
    {
          {{config.crdtp.namespace}}::SpanFrom("{{command.name}}"),
          &DomainDispatcherImpl::{{command.name}}
    },
      {% endfor %}
    };
    return commands;
  }();
  return {{config.crdtp.namespace}}::FindByFirst<DomainDispatcherImpl::CallHandler>(*commands, command_name, nullptr);
}
}  // namespace

std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> DomainDispatcherImpl::Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) {
  CallHandler handler = CommandByName(command_name);
  if (!handler) return nullptr;

  return [this, handler](const {{config.crdtp.namespace}}::Dispatchable& dispatchable) {
    (this->*handler)(dispatchable);
  };
}

  {% for command in domain.commands %}
    {% set command_name_title = command.name | to_title_case %}
    {% if "redirect" in command %}{% continue %}{% endif %}
    {% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
    {% if protocol.is_async_command(domain.domain, command.name) %}

class {{command_name_title}}CallbackImpl : public Backend::{{command_name_title}}Callback, public DomainDispatcher::Callback {
public:
    {{command_name_title}}CallbackImpl(std::unique_ptr<DomainDispatcher::WeakPtr> backendImpl, int callId, {{config.crdtp.namespace}}::span<uint8_t> message)
        : DomainDispatcher::Callback(std::move(backendImpl), callId,
{{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), message) { }

    void sendSuccess(
      {%- for parameter in command.returns -%}
        {%- if "optional" in parameter -%}
        Maybe<{{protocol.resolve_type(parameter).raw_type}}> {{parameter.name}}
        {%- else -%}
        {{protocol.resolve_type(parameter).pass_type}} {{parameter.name}}
        {%- endif -%}
        {%- if not loop.last -%}, {% endif -%}
      {%- endfor -%}) override
    {
        {{config.crdtp.namespace}}::ObjectSerializer serializer;
        {% for parameter in command.returns %}
        serializer.AddField({{config.crdtp.namespace}}::MakeSpan("{{parameter.name}}"), {{parameter.name}});
        {% endfor %}
        sendIfActive(serializer.Finish(), DispatchResponse::Success());
    }

    void fallThrough() override
    {
        fallThroughIfActive();
    }

    void sendFailure(const DispatchResponse& response) override
    {
        DCHECK(response.IsError());
        sendIfActive(nullptr, response);
    }
};
    {% endif %}

namespace {

    {% if "parameters" in command %}
struct {{command.name}}Params : public {{config.crdtp.namespace}}::DeserializableProtocolObject<{{command.name}}Params> {
      {% for parameter in command.parameters %}
        {% set parameter_type = protocol.resolve_type(parameter) %}
        {% if parameter.optional %}
    Maybe<{{parameter_type.raw_type}}> {{parameter.name}};
        {% else %}
    {{parameter_type.type}} {{parameter.name}};
        {% endif %}
      {% endfor %}
    DECLARE_DESERIALIZATION_SUPPORT();
};

CRDTP_BEGIN_DESERIALIZER({{command.name}}Params)
      {% for parameter in command.parameters  | sort(attribute = 'name', case_sensitive=True) %}
      {% if parameter.optional %}
    CRDTP_DESERIALIZE_FIELD_OPT("{{parameter.name}}", {{parameter.name}}),
      {% else %}
    CRDTP_DESERIALIZE_FIELD("{{parameter.name}}", {{parameter.name}}),
      {% endif %}
      {% endfor %}
CRDTP_END_DESERIALIZER()
     {% endif %}

}  // namespace

void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable)
{
    // Prepare input parameters.
    {% if "parameters" in command %}
    auto deserializer = {{config.crdtp.namespace}}::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer();
    {{command.name}}Params params;
    if (!{{command.name}}Params::Deserialize(&deserializer, &params)) {
      ReportInvalidParams(dispatchable, deserializer);
      return;
    }
    {% endif -%}

    {% if "returns" in command and not protocol.is_async_command(domain.domain, command.name) %}
    // Declare output parameters.
      {% for parameter in command.returns %}
        {% if "optional" in parameter %}
    Maybe<{{protocol.resolve_type(parameter).raw_type}}> out_{{parameter.name}};
        {% else %}
    {{protocol.resolve_type(parameter).type}} out_{{parameter.name}};
        {% endif %}
      {% endfor %}
    {% endif %}

    {% if not protocol.is_async_command(domain.domain, command.name) %}
    std::unique_ptr<DomainDispatcher::WeakPtr> weak = weakPtr();
    DispatchResponse response = m_backend->{{command.name | to_method_case}}(
      {%- for parameter in command.parameters -%}
        {%- if not loop.first -%}, {% endif -%}
        {%- if "optional" in parameter -%}
        std::move(params.{{parameter.name}})
        {%- else -%}
        {{protocol.resolve_type(parameter).to_pass_type % ("params." + parameter.name)}}
        {%- endif -%}
      {%- endfor %}
      {%- if "returns" in command %}
        {%- for parameter in command.returns -%}
          {%- if not loop.first or command.parameters -%}, {% endif -%}
          &out_{{parameter.name}}
        {%- endfor %}
      {% endif %});
    if (response.IsFallThrough()) {
        channel()->FallThrough(dispatchable.CallId(), {{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), dispatchable.Serialized());
        return;
    }
      {% if "returns" in command %}
      if (weak->get()) {
        std::unique_ptr<{{config.crdtp.namespace}}::Serializable> result;
        if (response.IsSuccess()) {
          {{config.crdtp.namespace}}::ObjectSerializer serializer;
          {% for parameter in command.returns %}
          serializer.AddField({{config.crdtp.namespace}}::MakeSpan("{{parameter.name}}"), out_{{parameter.name}});
          {% endfor %}
          result = serializer.Finish();
        } else {
          result = Serializable::From({});
        }
        weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result));
      }
      {% else %}
    if (weak->get())
        weak->get()->sendResponse(dispatchable.CallId(), response);
      {% endif %}
    return;
    {% else %}
    m_backend->{{command.name | to_method_case}}(
      {%- for property in command.parameters -%}
        {%- if not loop.first -%}, {% endif -%}
        {%- if "optional" in property -%}
        std::move(params.{{property.name}})
        {%- else -%}
        {{protocol.resolve_type(property).to_pass_type % ("params." + property.name)}}
        {%- endif -%}
      {%- endfor -%}
        {%- if command.parameters -%}, {% endif -%}
        std::make_unique<{{command_name_title}}CallbackImpl>(weakPtr(), dispatchable.CallId(), dispatchable.Serialized()));
    {% endif %}
}
  {% endfor %}

namespace {
// This helper method (with a static map of redirects) is used from Dispatcher::wire
// immediately below.
const std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>, {{config.crdtp.namespace}}::span<uint8_t>>>& SortedRedirects() {
  static auto* redirects = [](){
    auto* redirects = new std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>, {{config.crdtp.namespace}}::span<uint8_t>>>{
      {% for command in domain.commands|sort(attribute="name",case_sensitive=True) %}
        {% if "redirect" in command %}
          { {{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), {{config.crdtp.namespace}}::SpanFrom("{{command.redirect}}.{{command.name}}") },
        {% endif %}
      {% endfor %}
    };
    return redirects;
  }();
  return *redirects;
}
}  // namespace

// static
void Dispatcher::wire(UberDispatcher* uber, Backend* backend)
{
    auto dispatcher = std::make_unique<DomainDispatcherImpl>(uber->channel(), backend);
    uber->WireBackend({{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}"), SortedRedirects(), std::move(dispatcher));
}

} // {{domain.domain}}
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}