// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_session_h #define dap_session_h #include "future.h" #include "io.h" #include "typeinfo.h" #include "typeof.h" #include namespace dap { // Forward declarations struct Request; struct Response; struct Event; // internal functionality namespace detail { template struct traits { static constexpr bool isRequest = std::is_base_of::value; static constexpr bool isResponse = std::is_base_of::value; static constexpr bool isEvent = std::is_base_of::value; }; // ArgTy::type resolves to the first argument type of the function F. // F can be a function, static member function, or lambda. template struct ArgTy { using type = typename ArgTy::type; }; template struct ArgTy { using type = typename std::decay::type; }; template struct ArgTy { using type = typename std::decay::type; }; } // namespace detail //////////////////////////////////////////////////////////////////////////////// // Error //////////////////////////////////////////////////////////////////////////////// // Error represents an error message in response to a DAP request. struct Error { Error() = default; Error(const std::string& error); Error(const char* msg, ...); // operator bool() returns true if there is an error. inline operator bool() const { return message.size() > 0; } std::string message; // empty represents success. }; //////////////////////////////////////////////////////////////////////////////// // ResponseOrError //////////////////////////////////////////////////////////////////////////////// // ResponseOrError holds either the response to a DAP request or an error // message. template struct ResponseOrError { using Request = T; inline ResponseOrError() = default; inline ResponseOrError(const T& response); inline ResponseOrError(const Error& error); inline ResponseOrError(const ResponseOrError& other); T response; Error error; // empty represents success. }; template ResponseOrError::ResponseOrError(const T& response) : response(response) {} template ResponseOrError::ResponseOrError(const Error& error) : error(error) {} template ResponseOrError::ResponseOrError(const ResponseOrError& other) : response(other.response), error(other.error) {} //////////////////////////////////////////////////////////////////////////////// // Session //////////////////////////////////////////////////////////////////////////////// // Session implements a DAP client or server endpoint. // The general usage is as follows: // (1) Create a session with Session::create(). // (2) Register request and event handlers with registerHandler(). // (3) Optionally register a protocol error handler with onError(). // (3) Bind the session to the remote endpoint with bind(). // (4) Send requests or events with send(). class Session { template using IsRequest = typename std::enable_if::isRequest>::type; template using IsEvent = typename std::enable_if::isEvent>::type; template using ArgTy = typename detail::ArgTy::type; public: virtual ~Session() = default; // ErrorHandler is the type of callback function used for reporting protocol // errors. using ErrorHandler = std::function; // create() constructs and returns a new Session. static std::unique_ptr create(); // onError() registers a error handler that will be called whenever a protocol // error is encountered. // Only one error handler can be bound at any given time, and later calls // will replace the existing error handler. virtual void onError(const ErrorHandler&) = 0; // registerHandler() registers a request handler for a specific request type. // The function F must have one of the following signatures: // ResponseOrError(const RequestType&) // ResponseType(const RequestType&) // Error(const RequestType&) template > inline IsRequest registerHandler(F&& handler); // registerHandler() registers a event handler for a specific event type. // The function F must have the following signature: // void(const EventType&) template > inline IsEvent registerHandler(F&& handler); // registerSentHandler() registers the function F to be called when a response // of the specific type has been sent. // The function F must have the following signature: // void(const ResponseOrError&) template ::Request> inline void registerSentHandler(F&& handler); // send() sends the request to the connected endpoint and returns a // future that is assigned the request response or error. template > future> send(const T& request); // send() sends the event to the connected endpoint. template > void send(const T& event); // bind() connects this Session to an endpoint. // bind() can only be called once. Repeated calls will raise an error, but // otherwise will do nothing. virtual void bind(const std::shared_ptr&, const std::shared_ptr&) = 0; inline void bind(const std::shared_ptr&); protected: using RequestSuccessCallback = std::function; using RequestErrorCallback = std::function; using GenericResponseHandler = std::function; using GenericRequestHandler = std::function; using GenericEventHandler = std::function; using GenericResponseSentHandler = std::function; virtual void registerHandler(const TypeInfo* typeinfo, const GenericRequestHandler& handler) = 0; virtual void registerHandler(const TypeInfo* typeinfo, const GenericEventHandler& handler) = 0; virtual void registerHandler(const TypeInfo* typeinfo, const GenericResponseSentHandler& handler) = 0; virtual bool send(const dap::TypeInfo* typeinfo, const void* request, const GenericResponseHandler& responseHandler) = 0; virtual bool send(const TypeInfo*, const void* event) = 0; }; template Session::IsRequest Session::registerHandler(F&& handler) { using ResponseType = typename T::Response; auto cb = [handler](const void* args, const RequestSuccessCallback& onSuccess, const RequestErrorCallback& onError) { ResponseOrError res = handler(*reinterpret_cast(args)); if (res.error) { onError(TypeOf::type(), res.error); } else { onSuccess(TypeOf::type(), &res.response); } }; const TypeInfo* typeinfo = TypeOf::type(); registerHandler(typeinfo, cb); } template Session::IsEvent Session::registerHandler(F&& handler) { auto cb = [handler](const void* args) { handler(*reinterpret_cast(args)); }; const TypeInfo* typeinfo = TypeOf::type(); registerHandler(typeinfo, cb); } template void Session::registerSentHandler(F&& handler) { auto cb = [handler](const void* response, const Error* error) { if (error != nullptr) { handler(ResponseOrError(*error)); } else { handler(ResponseOrError(*reinterpret_cast(response))); } }; const TypeInfo* typeinfo = TypeOf::type(); registerHandler(typeinfo, cb); } template future> Session::send(const T& request) { using Response = typename T::Response; promise> promise; const TypeInfo* typeinfo = TypeOf::type(); auto sent = send(typeinfo, &request, [=](const void* result, const Error* error) { if (error != nullptr) { promise.set_value(ResponseOrError(*error)); } else { promise.set_value(ResponseOrError( *reinterpret_cast(result))); } }); if (!sent) { promise.set_value(Error("Failed to send request")); } return promise.get_future(); } template void Session::send(const T& event) { const TypeInfo* typeinfo = TypeOf::type(); send(typeinfo, &event); } void Session::bind(const std::shared_ptr& rw) { bind(rw, rw); } } // namespace dap #endif // dap_session_h