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
+1
View File
@@ -41,6 +41,7 @@ defmodule MusicLibrary.MixProject do
{:yaml_elixir, "~> 2.11"},
{:finch, "~> 0.19.0"},
{:vix, "~> 0.30.0"},
{:nimble_parsec, "~> 1.4"},
# TODO bump on release to {:phoenix_live_view, "~> 1.0.0"},
{:phoenix_live_view, "~> 1.0.0-rc.1", override: true},
{:floki, ">= 0.30.0", only: :test},
+1
View File
@@ -29,6 +29,7 @@
"mox": {:hex, :mox, "1.2.0", "a2cd96b4b80a3883e3100a221e8adc1b98e4c3a332a8fc434c39526babafd5b3", [:mix], [{:nimble_ownership, "~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}], "hexpm", "c7b92b3cc69ee24a7eeeaf944cd7be22013c52fcb580c1f33f50845ec821089a"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_ownership": {:hex, :nimble_ownership, "1.0.0", "3f87744d42c21b2042a0aa1d48c83c77e6dd9dd357e425a038dd4b49ba8b79a1", [:mix], [], "hexpm", "7c16cc74f4e952464220a73055b557a273e8b1b7ace8489ec9d86e9ad56cb2cc"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"},
@@ -0,0 +1,5 @@
defmodule MusicLibrary.Records.SearchParserTest do
use ExUnit.Case, async: true
doctest MusicLibrary.Records.SearchParser
end