<commit_before>#ifndef BLED112CLIENT_H
#define BLED112CLIENT_H

#include "bleapi.h"
#include "serial.h"
#include "firstargument.h"

#include <functional>
#include <map>

class Bled112Client {
public:
    template <typename Function>
    using DisableIfFirstArgumentIsPartial = std::enable_if<!Partial<typename FirstArgument<Function>::type>::value>;

    template <typename Function>
    using EnableIfFirstArgumentIsPartial = std::enable_if<Partial<typename FirstArgument<Function>::type>::value>;

    Bled112Client(const Serial &socket)
        : socket(socket)
    { }

    template <typename T>
    void write(const T &);

    template <typename T>
    void write(const T &, const Buffer &);

    template <typename T>
    T read();

    template <typename T>
    T read(Buffer &leftover);

    template <typename... Functions>
    void read(const Functions&...);

private:
    template <typename T>
    Header readHeader();

    void dispatch(const Header &);

    template <typename Function, typename... Functions>
    auto dispatch(const Header &, const Function &, const Functions&...)
        ->  typename DisableIfFirstArgumentIsPartial<Function>::type;

    template <typename Function, typename... Functions>
    auto dispatch(const Header &, const Function &, const Functions&...)
        -> typename EnableIfFirstArgumentIsPartial<Function>::type;

    Serial socket;
};

template <typename T>
void Bled112Client::write(const T &payload)
{
    socket.write(pack(getHeader<T>()));
    socket.write(pack(payload));
}

template <typename T>
void Bled112Client::write(const T &payload, const Buffer &leftover)
{
    socket.write(pack(getHeader<T>(leftover.size())));
    socket.write(pack(payload));
    socket.write(leftover);
}

template <typename T>
Header Bled112Client::readHeader()
{
    const auto header = unpack<Header>(socket.read(sizeof(Header)));
    if (header.cls != T::cls) {
        throw std::runtime_error("Class index does not match the expected value.");
    }
    if (header.cmd != T::cmd) {
        throw std::runtime_error("Command index does not match the expected value.");
    }
    if (!Partial<T>::value && header.length() != sizeof(T)) {
        throw std::runtime_error("Payload size does not match the expected value.");
    }

    return header;
}

template <typename T>
T Bled112Client::read()
{
    const auto header = readHeader<T>();
    const auto payload = unpack<T>(socket.read(header.length()));
    return payload;
}

template <typename T>
T Bled112Client::read(Buffer &leftover)
{
    const auto header = readHeader<T>();
    const auto payload = unpack<T>(socket.read(sizeof(T)));
    leftover = socket.read(header.length() - sizeof(T));

    return payload;
}

inline void Bled112Client::dispatch(const Header &) { }

template <typename Function, typename... Functions>
auto Bled112Client::dispatch(const Header &header, const Function &function, const Functions&... functions)
    -> typename DisableIfFirstArgumentIsPartial<Function>::type
{
    using arg_type = typename FirstArgument<Function>::type;

    if (arg_type::cls == header.cls && arg_type::cmd == header.cmd &&
            header.length() == sizeof(arg_type)) {
        function(unpack<arg_type>(socket.read(header.length())));
        return;
    }
    dispatch(header, functions...);
}

template <typename Function, typename... Functions>
auto Bled112Client::dispatch(const Header &header, const Function &function, const Functions&... functions)
    -> typename EnableIfFirstArgumentIsPartial<Function>::type
{
    using arg_type = typename FirstArgument<Function>::type;

    if (arg_type::cls == header.cls && arg_type::cmd == header.cmd) {
        const auto payload = unpack<arg_type>(socket.read(sizeof(arg_type)));
        const auto leftover = socket.read(header.length() - sizeof(arg_type));
        function(payload, leftover);
        return;
    }
    dispatch(header, functions...);
}

template <typename... Functions>
void Bled112Client::read(const Functions&... functions)
{
    const auto header = unpack<Header>(socket.read(sizeof(Header)));
    dispatch(header, functions...);
}

#endif // BLED112CLIENT_H<commit_msg>Tidy up the code, add license header<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BLED112CLIENT_H
#define BLED112CLIENT_H

#include "bleapi.h"
#include "firstargument.h"
#include "serial.h"

#include <functional>
#include <map>

class Bled112Client {
public:
    template <typename Function>
    using DisableIfFirstArgumentIsPartial = std::enable_if<!Partial<typename FirstArgument<Function>::type>::value>;

    template <typename Function>
    using EnableIfFirstArgumentIsPartial = std::enable_if<Partial<typename FirstArgument<Function>::type>::value>;

    Bled112Client(const Serial &socket)
        : socket(socket)
    { }

    template <typename T>
    void write(const T &);

    template <typename T>
    void write(const T &, const Buffer &);

    template <typename T>
    T read();

    template <typename T>
    T read(Buffer &);

    template <typename... Functions>
    void read(const Functions&...);

private:
    Header readHeader();

    template <typename T>
    void checkHeader(const Header &);

    template <typename T>
    T readPayload(const Header &);

    template <typename T>
    T readPayload(const Header &, Buffer &);

    void dispatch(const Header &);

    template <typename Function, typename... Functions>
    auto dispatch(const Header &, const Function &, const Functions&...)
        ->  typename DisableIfFirstArgumentIsPartial<Function>::type;

    template <typename Function, typename... Functions>
    auto dispatch(const Header &, const Function &, const Functions&...)
        -> typename EnableIfFirstArgumentIsPartial<Function>::type;

    Serial socket;
};

template <typename T>
void Bled112Client::write(const T &payload)
{
    socket.write(pack(getHeader<T>()));
    socket.write(pack(payload));
}

template <typename T>
void Bled112Client::write(const T &payload, const Buffer &leftover)
{
    socket.write(pack(getHeader<T>(leftover.size())));
    socket.write(pack(payload));
    socket.write(leftover);
}

inline Header Bled112Client::readHeader()
{
    return unpack<Header>(socket.read(sizeof(Header)));
}

template <typename T>
void Bled112Client::checkHeader(const Header &header)
{
    if (header.cls != T::cls) {
        throw std::runtime_error("Class index does not match the expected value.");
    }
    if (header.cmd != T::cmd) {
        throw std::runtime_error("Command index does not match the expected value.");
    }
    if (!Partial<T>::value && header.length() != sizeof(T)) {
        throw std::runtime_error("Payload size does not match the expected value.");
    }
}

template <typename T>
T Bled112Client::readPayload(const Header &header)
{
    return unpack<T>(socket.read(header.length()));
}

template <typename T>
T Bled112Client::readPayload(const Header &header, Buffer &leftover)
{
    const auto payload = unpack<T>(socket.read(sizeof(T)));
    leftover = socket.read(header.length() - sizeof(T));
    return payload;
}

template <typename T>
T Bled112Client::read()
{
    const auto header = readHeader();
    checkHeader<T>(header);
    return readPayload<T>(header);
}

template <typename T>
T Bled112Client::read(Buffer &leftover)
{
    const auto header = readHeader();
    checkHeader<T>(header);
    return readPayload<T>(header, leftover);
}

inline void Bled112Client::dispatch(const Header &) { }

template <typename Function, typename... Functions>
auto Bled112Client::dispatch(const Header &header, const Function &function, const Functions&... functions)
    -> typename DisableIfFirstArgumentIsPartial<Function>::type
{
    using T = typename FirstArgument<Function>::type;

    if (header.cls == T::cls && header.cmd == T::cmd && header.length() == sizeof(T)) {
        function(readPayload<T>(header));
        return;
    }
    dispatch(header, functions...);
}

template <typename Function, typename... Functions>
auto Bled112Client::dispatch(const Header &header, const Function &function, const Functions&... functions)
    -> typename EnableIfFirstArgumentIsPartial<Function>::type
{
    using T = typename FirstArgument<Function>::type;

    if (header.cls == T::cls && header.cmd == T::cmd) {
        Buffer leftover;
        const auto payload = readPayload<T>(header, leftover);

        function(std::move(payload), std::move(leftover));
        return;
    }
    dispatch(header, functions...);
}

// The dispatch works by iterating over a list of functions, the right one is selected based on the first argument.
// In the case that the data type is partial an additional argument is required to pass the payload.
//
// Accepted function signatures:
// void(Type)
// void(Type, Buffer)

template <typename... Functions>
void Bled112Client::read(const Functions&... functions)
{
    const auto header = readHeader();
    dispatch(header, functions...);
}

#endif // BLED112CLIENT_H
{'fraction_non_alphanumeric': 0.13336980306345733, 'fraction_numerical': 0.010612691466083152, 'mean_word_length': 4.0225274725274724, 'pattern_counts': {'":': 0, '<': 94, '<?xml version=': 0, '>': 102, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5858760', 'n_tokens_mistral': 2756, 'n_tokens_neox': 2570, 'n_words': 717}