Skip to content

Commit a258698

Browse files
authored
Merge pull request #46 from garyb/query-parsing-fixes
Query parsing fixes
2 parents 3b86c48 + 64b62fa commit a258698

File tree

3 files changed

+61
-52
lines changed

3 files changed

+61
-52
lines changed

bower.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"purescript-integers": "^3.0.0"
4242
},
4343
"devDependencies": {
44-
"purescript-console": "^3.0.0"
44+
"purescript-console": "^3.0.0",
45+
"purescript-assert": "^3.0.0"
4546
}
4647
}

src/Routing/Parser.purs

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,45 @@
1-
module Routing.Parser (
2-
parse
3-
) where
1+
module Routing.Parser (parse) where
42

5-
import Control.MonadPlus (guard)
3+
import Prelude
4+
5+
import Routing.Types (Route, RoutePart(..))
66
import Data.Array as A
7-
import Data.Either (fromRight)
8-
import Data.List (fromFoldable, List)
97
import Data.Map as M
10-
import Data.Maybe (Maybe, fromMaybe)
118
import Data.String as S
12-
import Data.String.Regex (Regex, regex, split) as R
13-
import Data.String.Regex.Flags (noFlags) as R
9+
import Control.MonadPlus (guard)
10+
import Data.List as L
11+
import Data.Maybe (Maybe(..))
1412
import Data.Traversable (traverse)
1513
import Data.Tuple (Tuple(..))
16-
import Partial.Unsafe (unsafePartial)
17-
import Prelude (map, discard, (>>>), ($), (<<<), (==), (<*>), (<$>), (<=))
18-
import Routing.Types (Route, RoutePart(..))
1914

20-
-- | Parse part of hash. Will return `Query (Map String String)` for query
15+
-- | Parse query part of hash. Will return `Map String String` for query
2116
-- | i.e. `"?foo=bar&bar=baz"` -->
22-
-- | `Query (fromList [Tuple "foo" "bar", Tuple "bar" "baz"])`
23-
parsePart :: String -> RoutePart
24-
parsePart str = fromMaybe (Path str) do
25-
guard $ S.take 1 str == "?"
26-
map (Query <<< M.fromFoldable)
27-
$ traverse part2tuple parts
17+
-- | `fromList [Tuple "foo" "bar", Tuple "bar" "baz"]`
18+
parseQueryPart :: (String -> String) -> String -> Maybe (M.Map String String)
19+
parseQueryPart decoder =
20+
map M.fromFoldable <<< traverse part2tuple <<< S.split (S.Pattern "&")
2821
where
29-
parts :: List String
30-
parts = fromFoldable $ S.split (S.Pattern "&") $ S.drop 1 str
31-
32-
part2tuple :: String -> Maybe (Tuple String String)
33-
part2tuple input = do
34-
let keyVal = S.split (S.Pattern "=") input
35-
guard $ A.length keyVal <= 2
36-
Tuple <$> (A.head keyVal) <*> (keyVal A.!! 1)
37-
38-
39-
splitRegex :: R.Regex
40-
splitRegex = unsafePartial fromRight $ R.regex "\\/|(?=\\?)" R.noFlags
22+
part2tuple :: String -> Maybe (Tuple String String)
23+
part2tuple input = do
24+
let keyVal = decoder <$> S.split (S.Pattern "=") input
25+
guard $ A.length keyVal <= 2
26+
Tuple <$> A.head keyVal <*> keyVal A.!! 1
4127

4228
-- | Parse hash string to `Route` with `decoder` function
4329
-- | applied to every hash part (usually `decodeURIComponent`)
4430
parse :: (String -> String) -> String -> Route
4531
parse decoder hash =
46-
map ( decoder >>> parsePart ) $ fromFoldable (R.split splitRegex hash)
32+
case flip S.splitAt hash =<< S.indexOf (S.Pattern "?") hash of
33+
Just { before, after } ->
34+
pathParts before
35+
<> map Query (L.fromFoldable (parseQueryPart decoder (S.drop 1 after)))
36+
Nothing ->
37+
pathParts hash
38+
where
39+
pathParts str =
40+
let
41+
parts = L.fromFoldable $ map Path (S.split (S.Pattern "/") str)
42+
in
43+
case L.unsnoc parts of
44+
Just { init, last: Path "" } -> init
45+
_ -> parts

test/Test/Main.purs

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
module Test.Main where
22

3-
import Prelude (class Show, Unit, discard, show, ($), (<$>), (*>), (<*), (<*>), (<>), append)
4-
import Control.Monad.Eff (Eff)
5-
import Control.Monad.Eff.Console (CONSOLE(), log)
3+
import Prelude
4+
65
import Control.Alt ((<|>))
6+
import Control.Monad.Eff (Eff)
7+
import Control.Monad.Eff.Console (CONSOLE, log)
8+
import Data.Either (Either(..))
79
import Data.List (List)
10+
import Data.List as L
811
import Data.Map as M
9-
10-
12+
import Data.Tuple (Tuple(..))
1113
import Routing (match)
1214
import Routing.Match (Match, list)
1315
import Routing.Match.Class (bool, end, int, lit, num, param, params)
16+
import Test.Assert (ASSERT, assert')
1417

1518
data FooBar
1619
= Foo Number (M.Map String String)
@@ -19,6 +22,8 @@ data FooBar
1922
| Quux Int
2023
| End Int
2124

25+
derive instance eqFooBar :: Eq FooBar
26+
2227
instance showFooBar :: Show FooBar where
2328
show (Foo num q) = "(Foo " <> show num <> " " <> show q <> ")"
2429
show (Bar bool str) = "(Bar " <> show bool <> " " <> show str <> ")"
@@ -36,18 +41,22 @@ routing =
3641
<|> Baz <$> (list num)
3742

3843

39-
main :: Eff (console :: CONSOLE) Unit
44+
main :: Eff (assert :: ASSERT, console :: CONSOLE) Unit
4045
main = do
41-
print "Foo: " $ match routing "foo/12/?welp='hi'&b=false" -- foo
42-
print "Foo: " $ match routing "foo/12?welp='hi'&b=false" -- foo
43-
print "Quux: " $ match routing "/quux/42" -- quux
44-
print "Baz: " $ match routing "/123/" -- baz
45-
print "End: " $ match routing "/1" -- end
46-
47-
where print s e = log $ append s $ show e
48-
49-
-- (minimal test for browser)
50-
51-
-- matches routing $ \old new -> void do
52-
-- logShow old
53-
-- logShow new
46+
assertEq (match routing "foo/12/?welp='hi'&b=false") (Right (Foo 12.0 (M.fromFoldable [Tuple "welp" "'hi'", Tuple "b" "false"])))
47+
assertEq (match routing "foo/12?welp='hi'&b=false") (Right (Foo 12.0 (M.fromFoldable [Tuple "welp" "'hi'", Tuple "b" "false"])))
48+
assertEq (match routing "/quux/42") (Right (Quux 42))
49+
assertEq (match routing "123/") (Right (Baz (L.fromFoldable [123.0])))
50+
assertEq (match routing "/1") (Right (End 1))
51+
assertEq (match routing "foo/0/?test=a/b/c") (Right (Foo 0.0 (M.fromFoldable [Tuple "test" "a/b/c"])))
52+
53+
assertEq
54+
:: forall a eff
55+
. Eq a
56+
=> Show a
57+
=> a
58+
-> a
59+
-> Eff (assert :: ASSERT, console :: CONSOLE | eff) Unit
60+
assertEq actual expected
61+
| actual /= expected = assert' ("Equality assertion failed\n\nActual: " <> show actual <> "\n\nExpected: " <> show expected) false
62+
| otherwise = log ("Equality assertion passed for " <> show actual)

0 commit comments

Comments
 (0)