@@ -21,6 +21,8 @@ module Web.HttpApiData (
2121 readEitherUrlPiece,
2222) where
2323
24+import Control.Applicative
25+
2426import Data.Monoid
2527import Data.ByteString (ByteString)
2628
@@ -73,11 +75,15 @@ class FromHttpApiData a where
7375 parseQueryParam :: Text -> Either Text a
7476 parseQueryParam = parseUrlPiece
7577
78+-- | Default parsing error.
79+defaultParseError :: Text -> Either Text a
80+defaultParseError input = Left ("could not convert: `" <> input <> "'")
81+
7682-- | Convert @'Maybe'@ parser into @'Either' 'Text'@ parser with default error message.
7783parseMaybeHttpApiData :: (Text -> Maybe a) -> (Text -> Either Text a)
7884parseMaybeHttpApiData parse input =
7985 case parse input of
80− Nothing -> Left ("could not convert: `" <> input <> "'")
86+ Nothing -> defaultParseError input
8187 Just val -> Right val
8288
8389-- | Convert to URL piece using @'Show'@ instance.
@@ -99,7 +105,7 @@ runReader reader input =
99105 Left err -> Left (T.pack err)
100106 Right (x, rest)
101107 | T.null rest -> Right x
102− | otherwise -> Left ("could not convert: `" <> input <> "'")
108+ | otherwise -> defaultParseError input
103109
104110-- | Run @'Reader'@ to parse bounded integral value with bounds checking.
105111parseBounded :: forall a. (Bounded a, Integral a) => Reader Integer -> Text -> Either Text a
@@ -112,6 +118,12 @@ parseBounded reader input = do
112118 l = toInteger (minBound :: a)
113119 h = toInteger (maxBound :: a)
114120
121+-- |
122+-- >>> toUrlPiece ()
123+-- "_"
124+instance ToHttpApiData () where
125+ toUrlPiece () = "_"
126+
115127instance ToHttpApiData Bool where toUrlPiece = showUrlPiece
116128instance ToHttpApiData Double where toUrlPiece = showUrlPiece
117129instance ToHttpApiData Float where toUrlPiece = showUrlPiece
@@ -131,6 +143,20 @@ instance ToHttpApiData Text where toUrlPiece = id
131143instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
132144instance ToHttpApiData Day where toUrlPiece = showUrlPiece
133145
146+-- |
147+-- >>> toUrlPiece (Just "Hello")
148+-- "Just Hello"
149+instance ToHttpApiData a => ToHttpApiData (Maybe a) where
150+ toUrlPiece (Just x) = "Just " <> toUrlPiece x
151+ toUrlPiece Nothing = "Nothing"
152+
153+-- |
154+-- >>> parseUrlPiece "_" :: Either Text ()
155+-- Right ()
156+instance FromHttpApiData () where
157+ parseUrlPiece "_" = return ()
158+ parseUrlPiece s = defaultParseError s
159+
134160instance FromHttpApiData Bool where parseUrlPiece = readEitherUrlPiece
135161instance FromHttpApiData Double where parseUrlPiece = runReader rational
136162instance FromHttpApiData Float where parseUrlPiece = runReader rational
@@ -150,6 +176,16 @@ instance FromHttpApiData Text where parseUrlPiece = Right
150176instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
151177instance FromHttpApiData Day where parseUrlPiece = readEitherUrlPiece
152178
179+-- |
180+-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
181+-- Right (Just 123)
182+instance FromHttpApiData a => FromHttpApiData (Maybe a) where
183+ parseUrlPiece "Nothing" = return Nothing
184+ parseUrlPiece s =
185+ case T.stripPrefix "Just " s of
186+ Nothing -> defaultParseError s
187+ Just x -> Just <$> parseUrlPiece x
188+
153189-- $examples
154190--
155191-- Booleans: