Skip to content

Commit 0393a98

Browse files
committed
initial commit
0 parents  commit 0393a98

File tree

8 files changed

+309
-0
lines changed

8 files changed

+309
-0
lines changed

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
dist
2+
cabal-dev
3+
*.o
4+
*.hi
5+
*.chi
6+
*.chs.h
7+
*.dyn_o
8+
*.dyn_hi
9+
.virtualenv
10+
.hpc
11+
.hsenv
12+
.cabal-sandbox/
13+
cabal.sandbox.config
14+
*.prof
15+
*.aux
16+
*.hp
17+
stack.yaml
18+
.stack-work/

.travis.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
language: haskell
2+
ghc: 7.8
3+
4+
install:
5+
- cabal install --only-dependencies --enable-tests
6+
7+
script:
8+
- cabal configure --enable-tests && cabal build
9+
- cabal test --show-details=always
10+
11+
branches:
12+
only:
13+
- master

LICENSE

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Copyright (c) 2016, Athan Clark
2+
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright
9+
notice, this list of conditions and the following disclaimer.
10+
11+
* Redistributions in binary form must reproduce the above
12+
copyright notice, this list of conditions and the following
13+
disclaimer in the documentation and/or other materials provided
14+
with the distribution.
15+
16+
* Neither the name of Athan Clark nor the names of other
17+
contributors may be used to endorse or promote products derived
18+
from this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# quack
2+
3+
TODO: Write description here
4+
5+
## Installation
6+
7+
TODO: Write installation instructions here
8+
9+
## Usage
10+
11+
### Creating `x`
12+
13+
TODO: Write usage instructions here
14+
15+
### Combining `x`
16+
17+
TODO: Write usage instructions here
18+
19+
### Consuming `x`
20+
21+
TODO: Write usage instructions here
22+
23+
## How to run tests
24+
25+
```
26+
cabal configure --enable-tests && cabal build && cabal test
27+
```
28+
29+
## Contributing
30+
31+
TODO: Write contribution instructions here

quack.cabal

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Name: quack
2+
Version: 0.0.0
3+
Author: Athan Clark <athan.clark@gmail.com>
4+
Maintainer: Athan Clark <athan.clark@gmail.com>
5+
License: BSD3
6+
License-File: LICENSE
7+
-- Synopsis:
8+
-- Description:
9+
Cabal-Version: >= 1.10
10+
Build-Type: Simple
11+
12+
Library
13+
Default-Language: Haskell2010
14+
HS-Source-Dirs: src
15+
GHC-Options: -Wall
16+
Exposed-Modules: Data.Uri.Query
17+
Build-Depends: base >= 4.8 && < 5
18+
, mtl
19+
, attoparsec
20+
, aeson
21+
, errors
22+
, http-types
23+
, text
24+
, bytestring
25+
26+
Test-Suite spec
27+
Type: exitcode-stdio-1.0
28+
Default-Language: Haskell2010
29+
Hs-Source-Dirs: src
30+
, test
31+
Ghc-Options: -Wall
32+
Main-Is: Spec.hs
33+
Build-Depends: base
34+
, tasty
35+
, tasty-quickcheck
36+
, tasty-hunit
37+
, QuickCheck
38+
, quickcheck-instances
39+
40+
Source-Repository head
41+
Type: git
42+
-- Location:

src/Data/Uri/Query.hs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
{-# LANGUAGE
2+
GeneralizedNewtypeDeriving
3+
, FlexibleInstances
4+
#-}
5+
6+
module Data.Uri.Query where
7+
8+
9+
import qualified Data.Attoparsec.Text as Atto (Parser, parseOnly)
10+
import qualified Data.Aeson as Aeson (FromJSON, decode)
11+
import Network.HTTP.Types.URI
12+
13+
import Data.ByteString as BS
14+
import Data.Text as T
15+
import Data.Text.Encoding as T
16+
import Data.Text.Lazy as LT
17+
import Data.Text.Lazy.Encoding as LT
18+
import Data.Functor.Identity
19+
import Data.String (IsString (fromString))
20+
import Control.Applicative
21+
import Control.Monad.State
22+
import Control.Monad.Except
23+
import Control.Error (hush)
24+
25+
26+
27+
data ParserState = ParserState
28+
{ parserStateSoFar :: [(T.Text, Maybe T.Text)]
29+
, parserStateToParse :: [(T.Text, Maybe T.Text)]
30+
} deriving (Show, Eq)
31+
32+
33+
initParserState :: [(T.Text, Maybe T.Text)] -> ParserState
34+
initParserState xs = ParserState
35+
{ parserStateSoFar = []
36+
, parserStateToParse = xs
37+
}
38+
39+
40+
data ParserError
41+
= NoParse
42+
43+
44+
45+
newtype Parser a = Parser
46+
{ getParser :: StateT ParserState (ExceptT ParserError Identity) a
47+
} deriving (Functor, Applicative, Monad)
48+
49+
50+
runParser :: Parser a -> [(T.Text, Maybe T.Text)] -> Either ParserError a
51+
runParser p xs = fst <$> runParser' p (initParserState xs)
52+
53+
54+
runParser' :: Parser a -> ParserState -> Either ParserError (a, ParserState)
55+
runParser' (Parser p) =
56+
runIdentity . runExceptT . runStateT p
57+
58+
59+
instance Alternative Parser where
60+
empty = Parser $ StateT $ \_ -> throwError NoParse
61+
x <|> y = Parser $ do
62+
s <- get
63+
case runParser' x s of
64+
Right (x', s') -> do
65+
put s'
66+
pure x'
67+
Left _ ->
68+
case runParser' y s of
69+
Right (y', s') -> do
70+
put s'
71+
pure y'
72+
Left e -> throwError e
73+
74+
75+
unlabeled :: PieceParser a -> Parser a
76+
unlabeled (PieceParser f) = Parser $ do
77+
s <- get
78+
case parserStateToParse s of
79+
(x,x') : ss ->
80+
case f x of
81+
Nothing -> throwError NoParse
82+
Just y -> do put ParserState
83+
{ parserStateSoFar = (x,x') : parserStateSoFar s
84+
, parserStateToParse = ss
85+
}
86+
pure y
87+
_ -> throwError NoParse
88+
89+
90+
-- | Useless label
91+
(.=) :: PieceParser a -> PieceParser b -> Parser b
92+
(PieceParser l) .= (PieceParser f) = Parser $ do
93+
s <- get
94+
case parserStateToParse s of
95+
(x,x') : ss ->
96+
case l x of
97+
Nothing -> throwError NoParse
98+
Just _ ->
99+
case f =<< x' of
100+
Nothing -> throwError NoParse
101+
Just y -> do put ParserState
102+
{ parserStateSoFar = (x,x') : parserStateSoFar s
103+
, parserStateToParse = ss
104+
}
105+
pure y
106+
_ -> throwError NoParse
107+
108+
109+
overEquals :: (a -> b -> c) -> PieceParser a -> PieceParser b -> Parser c
110+
overEquals f (PieceParser l) (PieceParser r) = Parser $ do
111+
s <- get
112+
case parserStateToParse s of
113+
(x,x') : ss ->
114+
case l x of
115+
Nothing -> throwError NoParse
116+
Just l' ->
117+
case r =<< x' of
118+
Nothing -> throwError NoParse
119+
Just r' -> do put ParserState
120+
{ parserStateSoFar = (x,x') : parserStateSoFar s
121+
, parserStateToParse = ss
122+
}
123+
pure (f l' r')
124+
_ -> throwError NoParse
125+
126+
127+
128+
-- * Pieces
129+
130+
newtype PieceParser a = PieceParser
131+
{ getPieceParser :: T.Text -> Maybe a
132+
}
133+
134+
instance IsString (PieceParser T.Text) where
135+
fromString s = PieceParser $ \t -> t <$ guard (T.pack s == t)
136+
137+
fromUtf8 :: (T.Text -> Maybe a) -> PieceParser a
138+
fromUtf8 = PieceParser
139+
140+
attoparsec :: Atto.Parser a -> PieceParser a
141+
attoparsec p = fromUtf8 (hush . Atto.parseOnly p)
142+
143+
aeson :: Aeson.FromJSON a => PieceParser a
144+
aeson = fromUtf8 (Aeson.decode . LT.encodeUtf8 . LT.fromStrict)

test/Data/Url/QuerySpec.hs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module Data.Url.QuerySpec (spec) where
2+
3+
import Data.Url.Query
4+
5+
import Test.Tasty
6+
import Test.Tasty.QuickCheck as QC
7+
import Test.QuickCheck
8+
import Test.QuickCheck.Instances
9+
10+
11+
spec :: TestTree
12+
spec = testGroup "Data.Url.Query"
13+
[ QC.testProperty "`someFunction` should pass"
14+
someFunction
15+
]
16+
17+
someFunction :: Bool -> Property
18+
someFunction x = not (not $ x) === x

test/Spec.hs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Spec where
2+
3+
import Data.Url.QuerySpec
4+
5+
import Test.Tasty
6+
7+
8+
main :: IO ()
9+
main = defaultMain tests
10+
11+
tests :: TestTree
12+
tests = testGroup "Testing..."
13+
[spec]

0 commit comments

Comments
 (0)