This commit adds a new protocol command to toggle protocol features

for a client connection. It works like the tag_mask and the associated
tagtypes command.

New commands:

- protocol
  Shows enabled protocol features.

- protocol available
  Show all available protocol features.

- protocol enable {feature...}
  Enables protocol features.

- protocol disable {feature...}
  Disables protocol features.

- protocol all
  Enables all available protocol features.

- protocol clear
  Disables all protocol features.

This commit adds also the first protocol feature.

hide_playlists_in_root
  Disables the listing of playlists in the root folder
  for the lsinfo command.
This commit is contained in:
jcorporation
2024-09-28 16:36:43 +02:00
parent 124c0e66ee
commit 23c2bba483
10 changed files with 322 additions and 2 deletions

View File

@@ -5,6 +5,7 @@
#include "IClient.hxx"
#include "Message.hxx"
#include "ProtocolFeature.hxx"
#include "command/CommandResult.hxx"
#include "command/CommandListBuilder.hxx"
#include "input/LastInputStream.hxx"
@@ -111,6 +112,11 @@ private:
*/
std::unique_ptr<BackgroundCommand> background_command;
/**
* Bitmask of protocol features.
*/
ProtocolFeature protocol_feature = ProtocolFeature::None();
public:
Client(EventLoop &loop, Partition &partition,
UniqueSocketDescriptor fd, int uid,
@@ -167,6 +173,29 @@ public:
permission = _permission;
}
ProtocolFeature GetProtocolFeatures() const noexcept {
return protocol_feature;
}
void SetProtocolFeatures(ProtocolFeature features, bool enable) noexcept {
if (enable)
protocol_feature.Set(features);
else
protocol_feature.Unset(features);
}
void AllProtocolFeatures() noexcept {
protocol_feature.SetAll();
}
void ClearProtocolFeatures() noexcept {
protocol_feature.Clear();
}
bool ProtocolFeatureEnabled(enum ProtocolFeatureType value) noexcept {
return protocol_feature.Test(value);
}
/**
* Send "idle" response to this client.
*/

View File

@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project
#include "ProtocolFeature.hxx"
#include "Client.hxx"
#include "Response.hxx"
#include "util/StringAPI.hxx"
#include <cassert>
#include <fmt/format.h>
struct feature_type_table {
const char *name;
ProtocolFeatureType type;
};
static constexpr struct feature_type_table protocol_feature_names_init[] = {
{"hide_playlists_in_root", PF_HIDE_PLAYLISTS_IN_ROOT},
};
/**
* This function converts the #tag_item_names_init array to an
* associative array at compile time. This is a kludge because C++20
* doesn't support designated initializers for arrays, unlike C99.
*/
static constexpr auto
MakeProtocolFeatureNames() noexcept
{
std::array<const char *, PF_NUM_OF_ITEM_TYPES> result{};
static_assert(std::size(protocol_feature_names_init) == result.size());
for (const auto &i : protocol_feature_names_init) {
/* no duplicates allowed */
assert(result[i.type] == nullptr);
result[i.type] = i.name;
}
return result;
}
constinit const std::array<const char *, PF_NUM_OF_ITEM_TYPES> protocol_feature_names = MakeProtocolFeatureNames();
void
protocol_features_print(Client &client, Response &r) noexcept
{
const auto protocol_feature = client.GetProtocolFeatures();
for (unsigned i = 0; i < PF_NUM_OF_ITEM_TYPES; i++)
if (protocol_feature.Test(ProtocolFeatureType(i)))
r.Fmt(FMT_STRING("feature: {}\n"), protocol_feature_names[i]);
}
void
protocol_features_print_all(Response &r) noexcept
{
for (unsigned i = 0; i < PF_NUM_OF_ITEM_TYPES; i++)
r.Fmt(FMT_STRING("feature: {}\n"), protocol_feature_names[i]);
}
ProtocolFeatureType
protocol_feature_parse_i(const char *name) noexcept
{
for (unsigned i = 0; i < PF_NUM_OF_ITEM_TYPES; ++i) {
assert(protocol_feature_names[i] != nullptr);
if (StringIsEqualIgnoreCase(name, protocol_feature_names[i]))
return (ProtocolFeatureType)i;
}
return PF_NUM_OF_ITEM_TYPES;
}

View File

@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project
#pragma once
#include <string_view>
#include <cstdint>
class Client;
class Response;
/**
* Codes for the type of a protocol feature.
*/
enum ProtocolFeatureType : uint8_t {
PF_HIDE_PLAYLISTS_IN_ROOT,
PF_NUM_OF_ITEM_TYPES
};
class ProtocolFeature {
using protocol_feature_t = uint_least8_t;
/* must have enough bits to represent all protocol features
supported by MPD */
static_assert(PF_NUM_OF_ITEM_TYPES <= sizeof(protocol_feature_t) * 8);
protocol_feature_t value;
explicit constexpr ProtocolFeature(protocol_feature_t _value) noexcept
:value(_value) {}
public:
constexpr ProtocolFeature() noexcept = default;
constexpr ProtocolFeature(ProtocolFeatureType _value) noexcept
:value(protocol_feature_t(1) << protocol_feature_t(_value)) {}
static constexpr ProtocolFeature None() noexcept {
return ProtocolFeature(protocol_feature_t(0));
}
static constexpr ProtocolFeature All() noexcept {
return ~None();
}
constexpr ProtocolFeature operator~() const noexcept {
return ProtocolFeature(~value);
}
constexpr ProtocolFeature operator&(ProtocolFeature other) const noexcept {
return ProtocolFeature(value & other.value);
}
constexpr ProtocolFeature operator|(ProtocolFeature other) const noexcept {
return ProtocolFeature(value | other.value);
}
constexpr ProtocolFeature operator^(ProtocolFeature other) const noexcept {
return ProtocolFeature(value ^ other.value);
}
constexpr ProtocolFeature &operator&=(ProtocolFeature other) noexcept {
value &= other.value;
return *this;
}
constexpr ProtocolFeature &operator|=(ProtocolFeature other) noexcept {
value |= other.value;
return *this;
}
constexpr ProtocolFeature &operator^=(ProtocolFeature other) noexcept {
value ^= other.value;
return *this;
}
constexpr bool TestAny() const noexcept {
return value != 0;
}
constexpr bool Test(ProtocolFeatureType feature) const noexcept {
return (*this & feature).TestAny();
}
constexpr void Set(ProtocolFeature features) noexcept {
*this |= features;
}
constexpr void Unset(ProtocolFeature features) noexcept {
*this &= ~ProtocolFeature(features);
}
constexpr void SetAll() noexcept {
*this = ProtocolFeature::All();
}
constexpr void Clear() noexcept {
*this = ProtocolFeature::None();
}
};
void
protocol_features_print(Client &client, Response &r) noexcept;
void
protocol_features_print_all(Response &r) noexcept;
ProtocolFeatureType
protocol_feature_parse_i(const char *name) noexcept;