Merge branch 'explicit_case_sensitivity' of https://github.com/geneticdrift/MPD

This commit is contained in:
Max Kellermann 2024-07-10 15:33:47 +02:00
commit 8861279add
2 changed files with 82 additions and 1 deletions

View File

@ -243,6 +243,43 @@ applies `Unicode normalization <https://unicode.org/reports/tr15/>`__
and converts all punctuation to ASCII equivalents
if MPD was compiled with `ICU <https://icu.unicode.org/>`__ support.
Explicit case-sensitivity [#since_0_24]_
----------------------------------------
.. note:: The following variants of filter operators override the default case sensitivity
that is command dependant with explicit case sensitivity.
.. list-table:: Explicitly case-sensitive operators
:widths: 33 33 33
* - Explicitly case-sensitive
- Explicitly case-insensitive
- Equivalent command dependant
* - ``eq_cs``
- ``eq_ci``
- ``==``
* - ``!eq_cs``
- ``!eq_ci``
- ``!=``
* - ``contains_cs``
- ``contains_ci``
- ``contains``
* - ``!contains_cs``
- ``!contains_ci``
- ``!contains``
* - ``starts_with_cs``
- ``starts_with_ci``
- ``starts_with``
* - ``!starts_with_cs``
- ``!starts_with_ci``
- ``!starts_with``
Prior to MPD 0.21, the syntax looked like this::
find TYPE VALUE

View File

@ -190,6 +190,39 @@ ExpectQuoted(const char *&s)
return {buffer, length};
}
/**
* Operator definition used to parse the operator
* from the command and create the StringFilter
* if it matched the operator prefix.
*/
struct OperatorDef {
const char *prefix;
bool fold_case;
bool negated;
StringFilter::Position position;
};
/**
* Pre-defined operators with explicit case-sensitivity.
*/
static constexpr std::array<OperatorDef, 12> operators = {
// operator prefix fold case negated position
OperatorDef { "contains_cs ", false, false, StringFilter::Position::ANYWHERE },
OperatorDef { "!contains_cs ", false, true, StringFilter::Position::ANYWHERE },
OperatorDef { "contains_ci ", true, false, StringFilter::Position::ANYWHERE },
OperatorDef { "!contains_ci ", true, true, StringFilter::Position::ANYWHERE },
OperatorDef { "starts_with_cs ", false, false, StringFilter::Position::PREFIX },
OperatorDef { "!starts_with_cs ", false, true, StringFilter::Position::PREFIX },
OperatorDef { "starts_with_ci ", true, false, StringFilter::Position::PREFIX },
OperatorDef { "!starts_with_ci ", true, true, StringFilter::Position::PREFIX },
OperatorDef { "eq_cs ", false, false, StringFilter::Position::FULL },
OperatorDef { "!eq_cs ", false, true, StringFilter::Position::FULL },
OperatorDef { "eq_ci ", true, false, StringFilter::Position::FULL },
OperatorDef { "!eq_ci ", true, true, StringFilter::Position::FULL },
};
/**
* Parse a string operator and its second operand and convert it to a
* #StringFilter.
@ -199,6 +232,17 @@ ExpectQuoted(const char *&s)
static StringFilter
ParseStringFilter(const char *&s, bool fold_case)
{
for (auto& op: operators) {
if (auto after_prefix = StringAfterPrefixIgnoreCase(s, op.prefix)) {
s = StripLeft(after_prefix);
return StringFilter(
ExpectQuoted(s),
op.fold_case,
op.position,
op.negated);
}
}
if (auto after_contains = StringAfterPrefixIgnoreCase(s, "contains ")) {
s = StripLeft(after_contains);
auto value = ExpectQuoted(s);
@ -260,7 +304,7 @@ ParseStringFilter(const char *&s, bool fold_case)
if (s[0] == '!' && s[1] == '=')
negated = true;
else if (s[0] != '=' || s[1] != '=')
throw std::runtime_error("'==' or '!=' expected");
throw FmtRuntimeError("Unknown filter operator: {}", s);
s = StripLeft(s + 2);
auto value = ExpectQuoted(s);