From d815928f91a9b279b200c0ddf48c17d89ca1fc49 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sun, 15 Mar 2026 18:56:30 +0000 Subject: [PATCH] Fix SearchParser catch-all wiping valid filters The `_, %{}` catch-all in `normalize/1` matched any map, so an invalid filter (e.g. `format:vin`) would reset the accumulator even when valid filters were already collected. Add a `map_size` guard so it only returns `%{query: ""}` for a truly empty accumulator. Also update the moduledoc. --- lib/music_library/records/search_parser.ex | 24 +++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/music_library/records/search_parser.ex b/lib/music_library/records/search_parser.ex index 36f29189..f2b5344c 100644 --- a/lib/music_library/records/search_parser.ex +++ b/lib/music_library/records/search_parser.ex @@ -1,10 +1,20 @@ defmodule MusicLibrary.Records.SearchParser do @moduledoc """ - This module includes functions to parse a search string containing tagged entities, - e.g. artists or albums. + Parses structured search queries into filter maps. - Implementation is most likely suboptimal, non-idiomatic and failing against specific - edge cases I haven't thought about - not an expert in parsing. + Supports the following filters: + + - `artist:value` — filter by artist name + - `album:value` — filter by album name + - `mbid:value` — filter by MusicBrainz ID + - `genre:value` — filter by genre + - `format:value` — filter by format (e.g. `cd`, `vinyl`) + - `type:value` — filter by type (e.g. `album`, `single`) + - `purchase_year:YYYY` — filter by purchase year + + Multi-word values can be quoted: `artist:"the pineapple thief"`. + Unrecognized format/type values are silently ignored. + Bare words become free-text query terms. """ @type search_result :: %{ @@ -105,6 +115,10 @@ defmodule MusicLibrary.Records.SearchParser do {:ok, %{query: ""}} iex> MusicLibrary.Records.SearchParser.parse("type:alb") {:ok, %{query: ""}} + iex> MusicLibrary.Records.SearchParser.parse("artist:marillion format:vin") + {:ok, %{artist: "marillion"}} + iex> MusicLibrary.Records.SearchParser.parse("artist:marillion type:alb") + {:ok, %{artist: "marillion"}} iex> MusicLibrary.Records.SearchParser.parse("type:album") {:ok, %{type: :album}} iex> MusicLibrary.Records.SearchParser.parse("purchase_year:2024") @@ -157,7 +171,7 @@ defmodule MusicLibrary.Records.SearchParser do {:query, [value]}, acc -> Map.update(acc, :query, value, &(&1 <> " " <> value)) - _, %{} -> + _, acc when map_size(acc) == 0 -> %{query: ""} _, acc ->