ADDED: A new IPC API based on protobuf and nng
Details, documentation, and language bindings are works in progress and will be evolving over the course of KiCad 9 development.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Jon Evans <[email protected]>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KICAD_API_HANDLER_H
|
||||
#define KICAD_API_HANDLER_H
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
#include <wx/debug.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
#include <import_export.h>
|
||||
#include <api/common/envelope.pb.h>
|
||||
#include <core/typeinfo.h>
|
||||
|
||||
using kiapi::common::ApiRequest, kiapi::common::ApiResponse;
|
||||
using kiapi::common::ApiResponseStatus, kiapi::common::ApiStatusCode;
|
||||
|
||||
typedef tl::expected<ApiResponse, ApiResponseStatus> API_RESULT;
|
||||
|
||||
template <typename T>
|
||||
using HANDLER_RESULT = tl::expected<T, ApiResponseStatus>;
|
||||
|
||||
class API_HANDLER
|
||||
{
|
||||
public:
|
||||
API_HANDLER() {}
|
||||
|
||||
/**
|
||||
* Attempt to handle the given API request, if a handler exists in this class for the message.
|
||||
* @param aMsg is a request to attempt to handle
|
||||
* @return a response to send to the client, or an appropriate error
|
||||
*/
|
||||
API_RESULT Handle( ApiRequest& aMsg );
|
||||
|
||||
static std::optional<KICAD_T> TypeNameFromAny( const google::protobuf::Any& aMessage );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* A handler for outer messages (envelopes) that will unpack to inner messages and call a
|
||||
* specific handler function. @see registerHandler.
|
||||
*/
|
||||
typedef std::function<HANDLER_RESULT<ApiResponse>( ApiRequest& )> REQUEST_HANDLER;
|
||||
|
||||
/**
|
||||
* Registers an API command handler for the given message types.
|
||||
*
|
||||
* When an API request matching the given type comes in, the handler will be called and its
|
||||
* response will be packed into an envelope for sending back to the API client.
|
||||
*
|
||||
* If the given message does not unpack into the request type, an envelope is returned with
|
||||
* status AS_BAD_REQUEST, which probably indicates corruption in the message.
|
||||
*
|
||||
* @tparam RequestType is a protobuf message type containing a command
|
||||
* @tparam ResponseType is a protobuf message type containing a command response
|
||||
* @tparam HandlerType is the implied type of the API_HANDLER subclass
|
||||
* @param aHandler is the handler function for the given request and response types
|
||||
*/
|
||||
template <class RequestType, class ResponseType, class HandlerType>
|
||||
void registerHandler( HANDLER_RESULT<ResponseType>( HandlerType::* aHandler )( RequestType& ) )
|
||||
{
|
||||
std::string typeName = RequestType().GetTypeName();
|
||||
|
||||
wxASSERT_MSG( !m_handlers.count( typeName ),
|
||||
wxString::Format( "Duplicate API handler for type %s", typeName ) );
|
||||
|
||||
m_handlers[typeName] =
|
||||
[=]( ApiRequest& aRequest ) -> API_RESULT
|
||||
{
|
||||
RequestType command;
|
||||
ApiResponse envelope;
|
||||
|
||||
if( !tryUnpack( aRequest, envelope, command ) )
|
||||
return envelope;
|
||||
|
||||
HANDLER_RESULT<ResponseType> response =
|
||||
std::invoke( aHandler, static_cast<HandlerType*>( this ), command );
|
||||
|
||||
if( response.has_value() )
|
||||
{
|
||||
envelope.mutable_status()->set_status( ApiStatusCode::AS_OK );
|
||||
envelope.mutable_message()->PackFrom( *response );
|
||||
return envelope;
|
||||
}
|
||||
else
|
||||
{
|
||||
return tl::unexpected( response.error() );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Maps type name (without the URL prefix) to a handler method
|
||||
std::map<std::string, REQUEST_HANDLER> m_handlers;
|
||||
|
||||
private:
|
||||
|
||||
template<typename MessageType>
|
||||
bool tryUnpack( ApiRequest& aRequest, ApiResponse& aReply, MessageType& aDest )
|
||||
{
|
||||
if( !aRequest.message().UnpackTo( &aDest ) )
|
||||
{
|
||||
std::string msg = fmt::format( "could not unpack message of type {} from request",
|
||||
aDest.GetTypeName() );
|
||||
aReply.mutable_status()->set_status( ApiStatusCode::AS_BAD_REQUEST );
|
||||
aReply.mutable_status()->set_error_message( msg );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //KICAD_API_HANDLER_H
|
||||
Reference in New Issue
Block a user