<commit_before>module Node.Express.Internal.QueryString
    ( Param(..)
    , parse
    , getOne
    , getAll
    ) where

import Prelude
import Data.Array (head, many, mapMaybe, some)
import Data.List (List, toUnfoldable)
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.String (fromCharArray)
import Text.Parsing.Parser (Parser, parseErrorMessage, runParser)
import Text.Parsing.Parser.Combinators (sepBy)
import Text.Parsing.Parser.String (satisfy, string)


data Param = Param String String

instance showParam :: Show Param where
    show (Param name val) = "(" <> show name <> " -> " <> show val <> ")"

instance eqParam :: Eq Param where
    eq (Param n1 v1) (Param n2 v2) = (n1 == n2) && (v1 == v2)

getOne :: (Array Param) -> String -> Maybe String
getOne params key = head $ getAll params key

getAll :: (Array Param) -> String -> (Array String)
getAll params key =
    mapMaybe (\(Param name val) -> if name == key then Just val else Nothing) params

parse :: String -> Either String (Array Param)
parse str = case runParser str queryString of
    Left err -> Left $ parseErrorMessage err
    Right result -> Right $ toUnfoldable result

queryString :: Parser String (List Param)
queryString = sepBy param (string "&")

param :: Parser String Param
param = do
    name <- liftM1 (decode <<< fromCharArray) $ some $ satisfy (\s -> s /= '=')
    _ <- string "="
    val  <- liftM1 (decode <<< fromCharArray) $ many $ satisfy (\s -> s /= '&')
    pure $ Param name val

foreign import decode :: String -> String<commit_msg>Fix #80: Avoid overflowing stack when parsing query params<commit_after>module Node.Express.Internal.QueryString
    ( Param(..)
    , parse
    , getOne
    , getAll
    ) where

import Prelude

import Control.Monad.Trampoline (runTrampoline)
import Data.Array (head, many, mapMaybe, some)
import Data.Either (Either(..))
import Data.List (List, toUnfoldable)
import Data.Maybe (Maybe(..))
import Data.String (fromCharArray)
import Text.Parsing.Parser (ParserT, parseErrorMessage, runParserT)
import Text.Parsing.Parser.Combinators (sepBy)
import Text.Parsing.Parser.String (class StringLike, satisfy, string)


data Param = Param String String

instance showParam :: Show Param where
    show (Param name val) = "(" <> show name <> " -> " <> show val <> ")"

instance eqParam :: Eq Param where
    eq (Param n1 v1) (Param n2 v2) = (n1 == n2) && (v1 == v2)

getOne :: (Array Param) -> String -> Maybe String
getOne params key = head $ getAll params key

getAll :: (Array Param) -> String -> (Array String)
getAll params key =
    mapMaybe (\(Param name val) -> if name == key then Just val else Nothing) params

parse :: String -> Either String (Array Param)
parse str = case runTrampoline $ runParserT str queryString of
    Left err -> Left $ parseErrorMessage err
    Right result -> Right $ toUnfoldable result

queryString :: ∀ m i. Monad m => StringLike i => ParserT i m (List Param)
queryString = sepBy param (string "&")

param :: ∀ m i. Monad m => StringLike i => ParserT i m Param
param = do
    name <- liftM1 (decode <<< fromCharArray) $ some $ satisfy (\s -> s /= '=')
    _ <- string "="
    val  <- liftM1 (decode <<< fromCharArray) $ many $ satisfy (\s -> s /= '&')
    pure $ Param name val

foreign import decode :: String -> String
{'fraction_non_alphanumeric': 0.12942243725430905, 'fraction_numerical': 0.006652555185969156, 'mean_word_length': 3.5006802721088435, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4728417', 'n_tokens_mistral': 1041, 'n_tokens_neox': 1031, 'n_words': 375}