Program Listing for File service.cpp

Return to documentation for file (foxglove/src/server/service.cpp)

#include <foxglove-c/foxglove-c.h>
#include <foxglove/server/service.hpp>

#include <memory>

using std::string_view_literals::operator""sv;

namespace foxglove {

void ServiceMessageSchema::writeTo(foxglove_service_message_schema* c) const noexcept {
  c->encoding = {this->encoding.data(), this->encoding.length()};
  c->schema.name = {this->schema.name.data(), this->schema.name.length()};
  c->schema.encoding = {this->schema.encoding.data(), this->schema.encoding.length()};
  c->schema.data = reinterpret_cast<const uint8_t*>(this->schema.data);
  c->schema.data_len = this->schema.data_len;
}

void ServiceSchema::writeTo(
  foxglove_service_schema* c, std::array<foxglove_service_message_schema, 2>& msg_schemas
) const noexcept {
  c->name = {this->name.data(), this->name.length()};
  if (this->request.has_value()) {
    this->request->writeTo(msg_schemas.data());
    c->request = msg_schemas.data();
  } else {
    c->request = nullptr;
  }
  if (this->response.has_value()) {
    this->response->writeTo(&msg_schemas[1]);
    c->response = &msg_schemas[1];
  } else {
    c->response = nullptr;
  }
}


ServiceRequest::ServiceRequest(const foxglove_service_request* r) noexcept
    : service_name(r->service_name.data, r->service_name.len)
    , client_id(r->client_id)
    , call_id(r->call_id)
    , encoding(r->encoding.data, r->encoding.len)
    , payload(
        reinterpret_cast<const std::byte*>(r->payload.data),
        reinterpret_cast<const std::byte*>(r->payload.data + r->payload.len)
      ) {}

void ServiceResponder::Deleter::operator()(foxglove_service_responder* ptr) const noexcept {
  auto message = "Internal server error: Service failed to send a response"sv;
  foxglove_service_respond_error(ptr, {message.data(), message.length()});
}

void ServiceResponder::respondOk(const std::byte* data, size_t size) && noexcept {
  auto* ptr = impl_.release();
  foxglove_service_respond_ok(ptr, {reinterpret_cast<const uint8_t*>(data), size});
}

void ServiceResponder::respondError(std::string_view message) && noexcept {
  auto* ptr = impl_.release();
  foxglove_service_respond_error(ptr, {message.data(), message.length()});
}

FoxgloveResult<Service> Service::create(
  std::string_view name, ServiceSchema& schema, ServiceHandler& handler
) {
  std::array<foxglove_service_message_schema, 2> msg_schemas{};
  foxglove_service_schema c_schema;
  schema.writeTo(&c_schema, msg_schemas);
  foxglove_service* ptr = nullptr;
  auto error = foxglove_service_create(
    &ptr,
    {name.data(), name.length()},
    &c_schema,
    &handler,
    [](
      const void* context,
      const foxglove_service_request* c_request,
      foxglove_service_responder* c_responder
    ) {
      const auto* handler = static_cast<const ServiceHandler*>(context);
      ServiceRequest request(c_request);
      ServiceResponder responder(c_responder);
      try {
        (*handler)(request, std::move(responder));
      } catch (const std::exception& exc) {
        warn() << "Service handler failed: " << exc.what();
      }
    }
  );
  if (error != foxglove_error::FOXGLOVE_ERROR_OK) {
    return foxglove::unexpected(static_cast<FoxgloveError>(error));
  }
  return Service(ptr);
}

void Service::Deleter::operator()(foxglove_service* ptr) const noexcept {
  foxglove_service_free(ptr);
}

foxglove_service* Service::release() noexcept {
  return impl_.release();
}

}  // namespace foxglove