152 lines
5.1 KiB
C++
152 lines
5.1 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
|
|
* 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 <kicommon.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>;
|
|
|
|
|
|
struct HANDLER_CONTEXT
|
|
{
|
|
std::string ClientName;
|
|
};
|
|
|
|
|
|
class KICOMMON_API API_HANDLER
|
|
{
|
|
public:
|
|
API_HANDLER() {}
|
|
|
|
virtual ~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 );
|
|
|
|
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&,
|
|
const HANDLER_CONTEXT& ) )
|
|
{
|
|
std::string typeName = RequestType().GetTypeName();
|
|
|
|
wxASSERT_MSG( !m_handlers.count( typeName ),
|
|
wxString::Format( "Duplicate API handler for type %s", typeName ) );
|
|
|
|
m_handlers[typeName] =
|
|
[this, &aHandler]( ApiRequest& aRequest ) -> API_RESULT
|
|
{
|
|
RequestType cmd;
|
|
ApiResponse envelope;
|
|
|
|
if( !tryUnpack( aRequest, envelope, cmd ) )
|
|
return envelope;
|
|
|
|
HANDLER_CONTEXT ctx;
|
|
ctx.ClientName = aRequest.header().client_name();
|
|
|
|
HANDLER_RESULT<ResponseType> response =
|
|
std::invoke( aHandler, static_cast<HandlerType*>( this ), cmd, ctx );
|
|
|
|
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;
|
|
|
|
static const wxString m_defaultCommitMessage;
|
|
|
|
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
|