/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 Jon Evans * 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 . */ #ifndef KICAD_API_HANDLER_H #define KICAD_API_HANDLER_H #include #include #include #include #include #include #include #include #include #include using kiapi::common::ApiRequest, kiapi::common::ApiResponse; using kiapi::common::ApiResponseStatus, kiapi::common::ApiStatusCode; typedef tl::expected API_RESULT; template using HANDLER_RESULT = tl::expected; 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( 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 void registerHandler( HANDLER_RESULT( 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 response = std::invoke( aHandler, static_cast( 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 m_handlers; static const wxString m_defaultCommitMessage; private: template 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