Implement basic parser

This commit is contained in:
Claudio Ortolina
2024-10-10 08:33:47 +01:00
parent 1fdf36ea8a
commit e30ba5ef3f
4 changed files with 69 additions and 0 deletions
@@ -0,0 +1,62 @@
defmodule MusicLibrary.Records.SearchParser do
import NimbleParsec
word = utf8_string([not: ?\s], min: 1)
space = ignore(string(" "))
quoted_words =
ignore(string(~s(")))
|> utf8_string([not: ?\"], min: 1)
|> ignore(string(~s(")))
query = choice([quoted_words, word]) |> tag(:query)
artist_filter = ignore(string("artist:"))
artist = concat(artist_filter, query) |> tag(:artist)
album_filter = ignore(string("album:"))
album = concat(album_filter, query) |> tag(:album)
mbid_filter = ignore(string("mbid:"))
mbid = concat(mbid_filter, query) |> tag(:mbid)
search = repeat(choice([artist, album, mbid, space, query]))
defparsecp(:search_parser, search)
@doc """
Parse a search query.
iex> MusicLibrary.Records.SearchParser.parse("")
{:ok, %{}}
iex> MusicLibrary.Records.SearchParser.parse("marbles")
{:ok, %{query: "marbles"}}
iex> MusicLibrary.Records.SearchParser.parse("artist:marillion album:marbles")
{:ok, %{artist: "marillion", album: "marbles"}}
iex> MusicLibrary.Records.SearchParser.parse("artist:marillion album:fugazi artist:fish")
{:ok, %{artist: "marillion fish", album: "fugazi"}}
"""
def parse(query) do
{:ok, result, _rest, _context, _line, _byte_offset} = search_parser(query)
{:ok, normalize(result)}
end
defp normalize(result) do
Enum.reduce(result, %{}, fn
{:artist, [{:query, [value]}]}, acc ->
Map.update(acc, :artist, value, &(&1 <> " " <> value))
{:album, [{:query, [value]}]}, acc ->
Map.update(acc, :album, value, &(&1 <> " " <> value))
{:mbid, [{:query, [value]}]}, acc ->
Map.put(acc, :mbid, value)
{:query, [value]}, acc ->
Map.update(acc, :query, value, &(&1 <> " " <> value))
_, acc ->
acc
end)
end
end