Purescript: how to parse a query string (in a url/uri)

Viewed 123

Purescript has a contrib package with URI.Query module: https://pursuit.purescript.org/packages/purescript-uri/8.0.1/docs/URI.Query for parsing it proposes to use Text.Parsing.Parser.

Could someone explain by the code example how to parse such query string: "s=str&i=1&b=true"

into a record {s :: String, i :: Int, b :: Maybe Boolean } (suppose, that the third param b is optional, may present or not)

1 Answers

Here is a basic version just using URI.Extra.QueryPair with some helpers to parse stuff:

module Main where

import Prelude

import Data.Either (hush)
import Data.Foldable (lookup)
import Data.Int (fromString)
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Class.Console (logShow)
import URI.Extra.QueryPairs (QueryPairs(..))
import URI.Extra.QueryPairs as QP
import URI.Query as Q

main :: Effect Unit
main = do
  logShow $ parseQuery "s=str&i=1&b=true"
  logShow $ parseQuery "s=abc&i=42&b=false"
  logShow $ parseQuery "s=str&i=1"

type Query = { s :: String, i :: Int, b :: Maybe Boolean }

parseQuery :: String -> Maybe Query
parseQuery = Q.fromString >>> QP.parse (QP.keyToString >>> pure) (QP.valueToString >>> pure) >>> hush >=> fromPairs

fromPairs :: QueryPairs String String -> Maybe Query
fromPairs (QueryPairs pairs) = do
  s <- join $ lookup "s" pairs
  i <- join (lookup "i" pairs) >>= fromString
  let b = join (lookup "b" pairs) >>= parseBool
  pure { s, i , b}
  where
  parseBool "true" = Just true
  parseBool "false" = Just false
  parseBool _ = Nothing

this will output

(Just { b: (Just true), i: 1, s: "str" })
(Just { b: (Just false), i: 42, s: "abc" })
(Just { b: Nothing, i: 1, s: "str" })

for the examples.


The code itself should be rather straight-forward. Note that QueryPair String String ~ Array String (Maybe String) so the extraction of values is a bit tedious (join)


a version using routing-duplex

If you are fine with installing purescript-routing-duplex you can use the parser in there to help you out quite a bit - that's the one I'd use (especially when you probably want to parse routes with parameters anyway):

module Main where

import Prelude

import Data.Either (hush)
import Data.Maybe (Maybe)
import Effect (Effect)
import Effect.Class.Console (logShow)
import Routing.Duplex as R

main :: Effect Unit
main = do
  logShow $ parseQuery "s=str&i=1&b=true"
  logShow $ parseQuery "s=abc&i=42&b=false"
  logShow $ parseQuery "s=str&i=1"

type Query = { s :: String, i :: Int, b :: Maybe Boolean }

parseQuery :: String -> Maybe Query
parseQuery s = hush $ R.parse parser ("?" <> s)
  where
    parser =
      R.params
        { s: R.string
        , i: R.int
        , b: R.optional <<< R.boolean
        }
Related