From 39e6b237af889f35192bba3b6598a8a90b56571f Mon Sep 17 00:00:00 2001 From: h7x4 Date: Mon, 8 Dec 2025 16:09:18 +0900 Subject: [PATCH] filter: document misc --- src/filter.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/filter.rs b/src/filter.rs index 9d47a2fd..7ec13eb6 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -1,3 +1,8 @@ +//! [`Filter`]s for querying the database and playlists. +//! +//! The filter syntax uses a context-free grammar, and has its own +//! set of parsers and parsing errors. + use std::fmt; use chrono::{DateTime, Utc}; @@ -9,6 +14,8 @@ use crate::{ types::{Priority, Tag}, }; +/// Represents the case sensitivity of a string comparison, +/// used in multiple filter variants. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum CaseSensitivity { CaseSensitive, @@ -16,6 +23,7 @@ pub enum CaseSensitivity { CommandDependent, } +/// Represents a comparison operator for priority comparisons. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ComparisonOperator { Equal, @@ -26,30 +34,88 @@ pub enum ComparisonOperator { LessThanOrEqual, } +/// Represents a filter expression for querying the music database. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Filter { + /// Logical NOT of a filter. e.g. + /// + /// `(!(Artist == "The Beatles"))` Not(Box), + + /// Logical AND of multiple filters. e.g. + /// + /// `((Artist == "The Beatles") AND (Album == "Abbey Road") AND (Title == "Come Together"))` And(Vec), - // The bool indicates whether the comparison is negated (true for !=, false for ==) + /// Equality comparison on a tag. e.g. + /// + /// `(Artist == "The Beatles")` or `(Album != "Greatest Hits")` + /// + /// The bool indicates whether the comparison is negated (true for !=, false for ==) EqTag(Tag, CaseSensitivity, bool), + + /// Substring containment on a tag. e.g. + /// + /// `(Title contains "Symphony")` or `(Album !contains "Live")` + /// + /// The bool indicates whether the comparison is negated (true for !contains, false for contains) Contains(Tag, CaseSensitivity, bool), + + /// Prefix matching on a tag. e.g. + /// + /// `(Title starts_with "Symphony")` or `(Album !starts_with "Live")` + //// + /// The bool indicates whether the comparison is negated (true for !starts_with, false for starts_with) StartsWith(Tag, CaseSensitivity, bool), + + /// Perl-compatible regular expression matching on a tag. e.g. + /// + /// `(Composer =~ "Beethoven.*")` or `(Genre !~ "Pop.*")` + /// + /// The bool indicates whether the comparison is negated (true for !~, false for =~) PerlRegex(Tag, bool), + + /// Equality comparison on the URI. e.g. + /// + /// `(uri == "Rock/Classics/track01.mp3")` EqUri(String), + + /// Base path filter. e.g. + /// + /// `(base "Rock/Classics")` Base(String), + + /// Filter for files added since the given timestamp. e.g. + /// + /// `(added-since '1622505600')` or `(added-since '2021-06-01T00:00:00Z')` AddedSince(DateTime), + + /// Filter for files modified since the given timestamp. e.g. + /// + /// `(modified-since '1622505600')` or `(modified-since '2021-06-01T00:00:00Z')` ModifiedSince(DateTime), + + /// Equality comparison on audio format. e.g. + /// + /// `(AudioFormat == '44100:16:2')` AudioFormatEq { sample_rate: u32, bits: u8, channels: u8, }, + + /// Masked equality comparison on audio format. e.g. + /// + /// `(AudioFormat =~ '44100:*:2')` AudioFormatEqMask { sample_rate: Option, bits: Option, channels: Option, }, + + /// Priority comparison. e.g. + /// + /// `(prio >= 42)` or `(prio != 10)` PrioCmp(ComparisonOperator, Priority), } @@ -243,6 +309,8 @@ impl Filter { // TODO: There is a significant amount of error handling to be improved and tested here. +// TODO: thiserror + #[derive(Debug, Clone, PartialEq)] pub enum FilterParserError<'a> { /// Could not parse the response due to a syntax error.