@@ -27,12 +27,13 @@ import Control.Monad.Catch
27import Control.Monad.Trans.Resource
27import Control.Monad.Trans.Resource
28import Data.Aeson
28import Data.Aeson
29import Data.ByteString (ByteString)
29import Data.ByteString (ByteString)
30+import qualified Data.ByteString.Char8 as BS8
30import qualified Data.ByteString.Lazy as LBS
31import qualified Data.ByteString.Lazy as LBS
32+import qualified Data.CaseInsensitive as CI
31import Data.Coerce
33import Data.Coerce
32import Data.Conduit
34import Data.Conduit
33import qualified Data.Conduit.List as CL
35import qualified Data.Conduit.List as CL
34import Data.Data
36import Data.Data
35−import Data.Default.Class (def)
36import Data.DList (DList)
37import Data.DList (DList)
37import qualified Data.DList as DList
38import qualified Data.DList as DList
38import Data.Foldable (foldMap, foldl')
39import Data.Foldable (foldMap, foldl')
@@ -42,12 +43,12 @@ import Data.Monoid
42import Data.Proxy
43import Data.Proxy
43import Data.Text (Text)
44import Data.Text (Text)
44import qualified Data.Text as Text
45import qualified Data.Text as Text
46+import qualified Data.Text.Encoding as Text
45import Data.Text.Lazy.Builder (Builder)
47import Data.Text.Lazy.Builder (Builder)
46import qualified Data.Text.Lazy.Builder as Build
48import qualified Data.Text.Lazy.Builder as Build
47import GHC.Generics
49import GHC.Generics
48import GHC.TypeLits
50import GHC.TypeLits
49import Network.HTTP.Client (HttpException, RequestBody (..))
51import Network.HTTP.Client (HttpException, RequestBody (..))
50−import qualified Network.HTTP.Client as Client
51import Network.HTTP.Media hiding (Accept)
52import Network.HTTP.Media hiding (Accept)
52import Network.HTTP.Types hiding (Header)
53import Network.HTTP.Types hiding (Header)
53import qualified Network.HTTP.Types as HTTP
54import qualified Network.HTTP.Types as HTTP
@@ -99,14 +100,11 @@ _Default = iso f Just
99 f (Just x) = x
100 f (Just x) = x
100 f Nothing = mempty
101 f Nothing = mempty
102−-- | A convenience alias to avoid type ambiguity.
103−type ClientRequest = Client.Request
105−-- | A convenience alias encapsulating the common 'Response'.
106−type ClientResponse = Client.Response Stream
108type Stream = ResumableSource (ResourceT IO) ByteString
103type Stream = ResumableSource (ResourceT IO) ByteString
105+newtype ServiceId = ServiceId Text
106+ deriving (Eq, Show, Data, Typeable)
110data Error
108data Error
111 = TransportError HttpException
109 = TransportError HttpException
112 | SerializeError SerializeError
110 | SerializeError SerializeError
@@ -116,20 +114,20 @@ data Error
116instance Exception Error
114instance Exception Error
118data SerializeError = SerializeError'
116data SerializeError = SerializeError'
117+ { _serializeId :: !ServiceId
119− -- , _serializeHeaders :: [Header]
118+ , _serializeHeaders :: [HTTP.Header]
120− { -- _serializeAbbrev :: !Abbrev
121− -- , _serializeStatus :: !Status
119+ , _serializeStatus :: !Status
120+ , _serializeMedia :: !MediaType
122− -- , _serializeMessage :: String
121+ , _serializeMessage :: String
123 } deriving (Eq, Show, Typeable)
122 } deriving (Eq, Show, Typeable)
125−data ServiceError = ServiceError' !Status !MediaType [HTTP.Header] LBS.ByteString
124+data ServiceError = ServiceError'
126− -- _serviceAbbrev :: !Abbrev
125+ { _serviceId :: !ServiceId
127− -- , _serviceStatus :: !Status
126+ , _serviceStatus :: !Status
128− -- , _serviceHeaders :: [Header]
127+ , _serviceHeaders :: [HTTP.Header]
129− -- , _serviceCode :: !ErrorCode
128+ , _serviceMedia :: !MediaType
130− -- , _serviceMessage :: Maybe ErrorMessage
129+ , _serviceBody :: LBS.ByteString
131− -- , _serviceRequestId :: Maybe RequestId
132− deriving (Eq, Show, Typeable)
130+ } deriving (Eq, Show, Typeable)
134class AsError a where
132class AsError a where
135 -- | A general Amazonka error.
133 -- | A general Amazonka error.
@@ -164,91 +162,103 @@ instance AsError Error where
164 ServiceError e -> Right e
162 ServiceError e -> Right e
165 x -> Left x
163 x -> Left x
167−-- | An intermediary request builder.
165+data Service = Service
168−data RequestBuilder = RequestBuilder
166+ { _svcId :: !ServiceId
169− { _rqHost :: ByteString
170− , _rqBasePath :: ByteString
167+ , _svcHost :: !ByteString
171− , _rqPath :: Builder
168+ , _svcPath :: !Builder
172− , _rqQuery :: DList (Text, Maybe Text)
169+ , _svcPort :: !Int
173− , _rqHeaders :: DList (Text, Text)
170+ , _svcSecure :: !Bool
174− , _rqBody :: Maybe (MediaType, Stream)
171+ , _svcTimeout :: !(Maybe Seconds)
172+ , _svcCheck :: !(Status -> Bool)
173+ , _svcRetry :: !(ServiceError -> Bool)
177−-- | Construct a default request builder. Since the Google service descriptions
176+-- | An intermediary request builder.
178−-- have http/https in the host (rootUrl), the scheme is assumed.
177+data Request = Request
179−defaultRequest :: ByteString -- ^ Host. (rootUrl)
180− -> ByteString -- ^ Path. (servicePath)
182−defaultRequest h p = RequestBuilder
185− , _rqPath = mempty
178+ { _rqPath :: Builder
186− , _rqQuery = mempty
179+ , _rqQuery :: DList (ByteString, Maybe ByteString)
187− , _rqHeaders = mempty
180+ , _rqHeaders :: DList (HeaderName, ByteString)
188− , _rqBody = Nothing
181+ , _rqBody :: Maybe (MediaType, RequestBody)
191−appendPath :: RequestBuilder -> Builder -> RequestBuilder
184+appendPath :: Request -> Builder -> Request
192appendPath rq x = rq { _rqPath = _rqPath rq <> "/" <> x }
185appendPath rq x = rq { _rqPath = _rqPath rq <> "/" <> x }
194−appendPaths :: ToText a => RequestBuilder -> [a] -> RequestBuilder
187+appendPaths :: ToText a => Request -> [a] -> Request
195appendPaths rq = appendPath rq . foldMap (mappend "/" . buildText)
188appendPaths rq = appendPath rq . foldMap (mappend "/" . buildText)
197−appendQuery :: RequestBuilder -> Text -> Maybe Text -> RequestBuilder
190+appendQuery :: Request -> ByteString -> Maybe Text -> Request
191+appendQuery rq k v = rq
198−appendQuery rq k v = rq { _rqQuery = DList.snoc (_rqQuery rq) (k, v) }
192+ { _rqQuery = DList.snoc (_rqQuery rq) (k, Text.encodeUtf8 <$> v)
200−appendHeader :: RequestBuilder -> Text -> Maybe Text -> RequestBuilder
195+appendHeader :: Request -> ByteString -> Maybe Text -> Request
201appendHeader rq _ Nothing = rq
196appendHeader rq _ Nothing = rq
197+appendHeader rq k (Just v) = rq
202−appendHeader rq k (Just v) = rq { _rqHeaders = DList.snoc (_rqHeaders rq) (k, v) }
198+ { _rqHeaders = DList.snoc (_rqHeaders rq) (CI.mk k, Text.encodeUtf8 v)
204−setStream :: RequestBuilder -> MediaType -> Stream -> RequestBuilder
201+setBody :: Request -> MediaType -> RequestBody -> Request
205−setStream rq c x = rq { _rqBody = Just (c, x) }
202+setBody rq c x = rq { _rqBody = Just (c, x) }
207-- | A materialised 'http-client' request and associated response parser.
204-- | A materialised 'http-client' request and associated response parser.
208data Client a = Client
205data Client a = Client
209− { _clientAccept :: Maybe MediaType
206+ { _cliAccept :: Maybe MediaType
207+ , _cliMethod :: Method
210− , _clientCheck :: Int -> Bool
208+ , _cliCheck :: Int -> Bool
209+ , _cliService :: Service
210+ , _cliRequest :: Request
211− , _clientResponse :: Stream -> ResourceT IO (Either SerializeError a)
211+ , _cliResponse :: Stream -> ResourceT IO (Either SerializeError a)
212− , _clientRequest :: ClientRequest
214+mime :: FromStream c a
221+mime p = client (fromStream p) (Just (contentType p))
228+discard = client (\b -> closeResumableSource b >> pure (Right ())) Nothing
215−clientRequest :: (Stream -> ResourceT IO (Either SerializeError a))
230+client :: (Stream -> ResourceT IO (Either SerializeError a))
216− -> Maybe MediaType
231+ -> Maybe MediaType
217− -> Method
232+ -> Method
218− -> [Int]
233+ -> [Int]
219− -> RequestBuilder
234+ -> Request
220− -> Client a
236+ -> Client a
221−clientRequest f cs m ns rq = Client
237+client f cs m ns rq s = Client
222− { _clientAccept = cs
238+ { _cliAccept = cs
223− , _clientCheck = (`elem` ns)
239+ , _cliMethod = m
224− , _clientResponse = f
240+ , _cliCheck = (`elem` ns)
225− , _clientRequest = def
241+ , _cliService = s
226− { Client.method = m
242+ , _cliRequest = rq
227− , Client.checkStatus = \_ _ _ -> Nothing
243+ , _cliResponse = f
231−clientMime :: FromStream c a
246+class Accept c => ToBody c a where
232− => Proxy c
247+ toBody :: Proxy c -> a -> RequestBody
237−clientMime p = clientRequest (fromStream p) (Just (contentType p))
239−clientEmpty :: Method -> [Int] -> RequestBuilder -> Client ()
249+instance ToBody OctetStream RequestBody where
240−clientEmpty = clientRequest go Nothing
250+ toBody Proxy = id
242− go b = closeResumableSource b >> pure (Right ())
244−class Accept c => ToStream c a where
252+instance ToBody OctetStream ByteString where toBody Proxy = RequestBodyBS
245− toStream :: Proxy c -> a -> Stream
253+instance ToBody OctetStream LBS.ByteString where toBody Proxy = RequestBodyLBS
254+instance ToBody PlainText ByteString where toBody Proxy = RequestBodyBS
255+instance ToBody PlainText LBS.ByteString where toBody Proxy = RequestBodyLBS
247−instance ToStream OctetStream Stream where
257+instance ToJSON a => ToBody JSON a where
248− toStream Proxy = id
258+ toBody Proxy = RequestBodyLBS . encode
250−instance ToJSON a => ToStream JSON a where
260+instance ToBody JSON Value where
251− toStream Proxy = newResumableSource . CL.sourceList . LBS.toChunks . encode
261+ toBody Proxy = RequestBodyLBS . encode
253class Accept c => FromStream c a where
263class Accept c => FromStream c a where
254 fromStream :: Proxy c
264 fromStream :: Proxy c
@@ -268,13 +278,12 @@ instance FromJSON a => FromStream JSON a where
268class GoogleRequest a where
278class GoogleRequest a where
269 type Rs a :: *
279 type Rs a :: *
271− request :: a -> Client (Rs a)
281+ requestClient :: a -> Client (Rs a)
272− requestWith :: RequestBuilder -> a -> Client (Rs a)
274class GoogleClient fn where
283class GoogleClient fn where
275 type Fn fn :: *
284 type Fn fn :: *
277− clientBuild :: Proxy fn -> RequestBuilder -> Fn fn
286+ buildClient :: Proxy fn -> Request -> Fn fn
279-- | Multiple path captures, with @[xs]@ forming @/<x1>/<x2>/<x2>/...@.
288-- | Multiple path captures, with @[xs]@ forming @/<x1>/<x2>/<x2>/...@.
280data Captures (s :: Symbol) a
289data Captures (s :: Symbol) a
@@ -340,15 +349,15 @@ instance ( MimeRender c m
340instance (KnownSymbol s, GoogleClient fn) => GoogleClient (s :> fn) where
349instance (KnownSymbol s, GoogleClient fn) => GoogleClient (s :> fn) where
341 type Fn (s :> fn) = Fn fn
350 type Fn (s :> fn) = Fn fn
343− clientBuild Proxy rq = clientBuild (Proxy :: Proxy fn) $
352+ buildClient Proxy rq = buildClient (Proxy :: Proxy fn) $
344 appendPath rq (buildSymbol (Proxy :: Proxy s))
353 appendPath rq (buildSymbol (Proxy :: Proxy s))
346instance (GoogleClient a, GoogleClient b) => GoogleClient (a :<|> b) where
355instance (GoogleClient a, GoogleClient b) => GoogleClient (a :<|> b) where
347 type Fn (a :<|> b) = Fn a :<|> Fn b
356 type Fn (a :<|> b) = Fn a :<|> Fn b
349− clientBuild Proxy rq =
358+ buildClient Proxy rq =
350− clientBuild (Proxy :: Proxy a) rq
359+ buildClient (Proxy :: Proxy a) rq
351− :<|> clientBuild (Proxy :: Proxy b) rq
360+ :<|> buildClient (Proxy :: Proxy b) rq
353instance ( KnownSymbol s
362instance ( KnownSymbol s
354 , ToText a
363 , ToText a
@@ -356,7 +365,7 @@ instance ( KnownSymbol s
356 ) => GoogleClient (Capture s a :> fn) where
365 ) => GoogleClient (Capture s a :> fn) where
357 type Fn (Capture s a :> fn) = a -> Fn fn
366 type Fn (Capture s a :> fn) = a -> Fn fn
359− clientBuild Proxy rq = clientBuild (Proxy :: Proxy fn)
368+ buildClient Proxy rq = buildClient (Proxy :: Proxy fn)
360 . appendPath rq
369 . appendPath rq
361 . buildText
370 . buildText
@@ -366,7 +375,7 @@ instance ( KnownSymbol s
366 ) => GoogleClient (Captures s a :> fn) where
375 ) => GoogleClient (Captures s a :> fn) where
367 type Fn (Captures s a :> fn) = [a] -> Fn fn
376 type Fn (Captures s a :> fn) = [a] -> Fn fn
369− clientBuild Proxy rq = clientBuild (Proxy :: Proxy fn)
378+ buildClient Proxy rq = buildClient (Proxy :: Proxy fn)
370 . appendPaths rq
379 . appendPaths rq
372instance ( KnownSymbol s
381instance ( KnownSymbol s
@@ -376,7 +385,7 @@ instance ( KnownSymbol s
376 ) => GoogleClient (CaptureMode s m a :> fn) where
385 ) => GoogleClient (CaptureMode s m a :> fn) where
377 type Fn (CaptureMode s m a :> fn) = a -> Fn fn
386 type Fn (CaptureMode s m a :> fn) = a -> Fn fn
379− clientBuild Proxy rq x = clientBuild (Proxy :: Proxy fn)
388+ buildClient Proxy rq x = buildClient (Proxy :: Proxy fn)
380 . appendPath rq
389 . appendPath rq
381 $ buildText x <> buildSymbol (Proxy :: Proxy m)
390 $ buildText x <> buildSymbol (Proxy :: Proxy m)
@@ -386,12 +395,12 @@ instance ( KnownSymbol s
386 ) => GoogleClient (QueryParam s a :> fn) where
395 ) => GoogleClient (QueryParam s a :> fn) where
387 type Fn (QueryParam s a :> fn) = Maybe a -> Fn fn
396 type Fn (QueryParam s a :> fn) = Maybe a -> Fn fn
389− clientBuild Proxy rq mx = clientBuild (Proxy :: Proxy fn) $
398+ buildClient Proxy rq mx = buildClient (Proxy :: Proxy fn) $
390 case mx of
399 case mx of
391 Nothing -> rq
400 Nothing -> rq
392 Just x -> appendQuery rq k v
401 Just x -> appendQuery rq k v
394− k = textSymbol (Proxy :: Proxy s)
403+ k = byteSymbol (Proxy :: Proxy s)
395 v = Just (toText x)
404 v = Just (toText x)
397instance ( KnownSymbol s
406instance ( KnownSymbol s
@@ -400,11 +409,11 @@ instance ( KnownSymbol s
400 ) => GoogleClient (QueryParams s a :> fn) where
409 ) => GoogleClient (QueryParams s a :> fn) where
401 type Fn (QueryParams s a :> fn) = [a] -> Fn fn
410 type Fn (QueryParams s a :> fn) = [a] -> Fn fn
403− clientBuild Proxy rq = clientBuild (Proxy :: Proxy fn) . foldl' go rq
412+ buildClient Proxy rq = buildClient (Proxy :: Proxy fn) . foldl' go rq
405 go r = appendQuery r k . Just . toText
414 go r = appendQuery r k . Just . toText
407− k = textSymbol (Proxy :: Proxy s)
416+ k = byteSymbol (Proxy :: Proxy s)
409instance ( KnownSymbol s
418instance ( KnownSymbol s
410 , ToText a
419 , ToText a
@@ -412,94 +421,94 @@ instance ( KnownSymbol s
412 ) => GoogleClient (Header s a :> fn) where
421 ) => GoogleClient (Header s a :> fn) where
413 type Fn (Header s a :> fn) = Maybe a -> Fn fn
422 type Fn (Header s a :> fn) = Maybe a -> Fn fn
415− clientBuild Proxy rq mx = clientBuild (Proxy :: Proxy fn) $
424+ buildClient Proxy rq mx = buildClient (Proxy :: Proxy fn) $
416 case mx of
425 case mx of
417 Nothing -> rq
426 Nothing -> rq
418 Just x -> appendHeader rq k v
427 Just x -> appendHeader rq k v
420− k = textSymbol (Proxy :: Proxy s)
429+ k = byteSymbol (Proxy :: Proxy s)
421 v = Just (toText x)
430 v = Just (toText x)
423−instance ( ToStream c a
432+instance ( ToBody c a
424 , GoogleClient fn
433 , GoogleClient fn
425 ) => GoogleClient (ReqBody (c ': cs) a :> fn) where
434 ) => GoogleClient (ReqBody (c ': cs) a :> fn) where
426 type Fn (ReqBody (c ': cs) a :> fn) = a -> Fn fn
435 type Fn (ReqBody (c ': cs) a :> fn) = a -> Fn fn
428− clientBuild Proxy rq = clientBuild (Proxy :: Proxy fn)
437+ buildClient Proxy rq = buildClient (Proxy :: Proxy fn)
429− . setStream rq (contentType p)
438+ . setBody rq (contentType p)
430− . toStream p
439+ . toBody p
432 p = Proxy :: Proxy c
441 p = Proxy :: Proxy c
434instance {-# OVERLAPPABLE #-}
443instance {-# OVERLAPPABLE #-}
435 FromStream c a => GoogleClient (Get (c ': cs) a) where
444 FromStream c a => GoogleClient (Get (c ': cs) a) where
436− type Fn (Get (c ': cs) a) = Client a
445+ type Fn (Get (c ': cs) a) = Service -> Client a
438− clientBuild Proxy = clientMime (Proxy :: Proxy c) methodGet [200, 203]
447+ buildClient Proxy = mime (Proxy :: Proxy c) methodGet [200, 203]
440instance {-# OVERLAPPING #-}
449instance {-# OVERLAPPING #-}
441 GoogleClient (Get (c ': cs) ()) where
450 GoogleClient (Get (c ': cs) ()) where
442− type Fn (Get (c ': cs) ()) = Client ()
451+ type Fn (Get (c ': cs) ()) = Service -> Client ()
444− clientBuild Proxy = clientEmpty methodGet [204]
453+ buildClient Proxy = discard methodGet [204]
446instance {-# OVERLAPPABLE #-}
455instance {-# OVERLAPPABLE #-}
447 (FromStream c a, cs' ~ (c ': cs)) => GoogleClient (Post cs' a) where
456 (FromStream c a, cs' ~ (c ': cs)) => GoogleClient (Post cs' a) where
448− type Fn (Post cs' a) = Client a
457+ type Fn (Post cs' a) = Service -> Client a
450− clientBuild Proxy = clientMime (Proxy :: Proxy c) methodPost [200, 201]
459+ buildClient Proxy = mime (Proxy :: Proxy c) methodPost [200, 201]
452instance {-# OVERLAPPING #-}
461instance {-# OVERLAPPING #-}
453 GoogleClient (Post cs ()) where
462 GoogleClient (Post cs ()) where
454− type Fn (Post cs ()) = Client ()
463+ type Fn (Post cs ()) = Service -> Client ()
456− clientBuild Proxy = clientEmpty methodPost [204]
465+ buildClient Proxy = discard methodPost [204]
458instance {-# OVERLAPPABLE #-}
467instance {-# OVERLAPPABLE #-}
459 FromStream c a => GoogleClient (Put (c ': cs) a) where
468 FromStream c a => GoogleClient (Put (c ': cs) a) where
460− type Fn (Put (c ': cs) a) = Client a
469+ type Fn (Put (c ': cs) a) = Service -> Client a
462− clientBuild Proxy = clientMime (Proxy :: Proxy c) methodPut [200, 201]
471+ buildClient Proxy = mime (Proxy :: Proxy c) methodPut [200, 201]
464instance {-# OVERLAPPING #-}
473instance {-# OVERLAPPING #-}
465 GoogleClient (Put (c ': cs) ()) where
474 GoogleClient (Put (c ': cs) ()) where
466− type Fn (Put (c ': cs) ()) = Client ()
475+ type Fn (Put (c ': cs) ()) = Service -> Client ()
468− clientBuild Proxy = clientEmpty methodPut [204]
477+ buildClient Proxy = discard methodPut [204]
470instance {-# OVERLAPPABLE #-}
479instance {-# OVERLAPPABLE #-}
471 FromStream c a => GoogleClient (Patch (c ': cs) a) where
480 FromStream c a => GoogleClient (Patch (c ': cs) a) where
472− type Fn (Patch (c ': cs) a) = Client a
481+ type Fn (Patch (c ': cs) a) = Service -> Client a
474− clientBuild Proxy = clientMime (Proxy :: Proxy c) methodPatch [200, 201]
483+ buildClient Proxy = mime (Proxy :: Proxy c) methodPatch [200, 201]
476instance {-# OVERLAPPING #-}
485instance {-# OVERLAPPING #-}
477 GoogleClient (Patch (c ': cs) ()) where
486 GoogleClient (Patch (c ': cs) ()) where
478− type Fn (Patch (c ': cs) ()) = Client ()
487+ type Fn (Patch (c ': cs) ()) = Service -> Client ()
480− clientBuild Proxy = clientEmpty methodPatch [204]
489+ buildClient Proxy = discard methodPatch [204]
482instance {-# OVERLAPPABLE #-}
491instance {-# OVERLAPPABLE #-}
483 FromStream c a => GoogleClient (Delete (c ': cs) a) where
492 FromStream c a => GoogleClient (Delete (c ': cs) a) where
484− type Fn (Delete (c ': cs) a) = Client a
493+ type Fn (Delete (c ': cs) a) = Service -> Client a
486− clientBuild Proxy = clientMime (Proxy :: Proxy c) methodDelete [200, 202]
495+ buildClient Proxy = mime (Proxy :: Proxy c) methodDelete [200, 202]
488instance {-# OVERLAPPING #-}
497instance {-# OVERLAPPING #-}
489 GoogleClient (Delete (c ': cs) ()) where
498 GoogleClient (Delete (c ': cs) ()) where
490− type Fn (Delete (c ': cs) ()) = Client ()
499+ type Fn (Delete (c ': cs) ()) = Service -> Client ()
492− clientBuild Proxy = clientEmpty methodDelete [204]
501+ buildClient Proxy = discard methodDelete [204]
494-- perform :: Accept
503-- perform :: Accept
495-- -> (Stream -> Client (Either SerializeError a))
504-- -> (Stream -> Client (Either SerializeError a))
496-- -> Method
505-- -> Method
497-- -> [Int]
506-- -> [Int]
498−-- -> RequestBuilder
507+-- -> Request
499-- -> Client a
508-- -> Client a
500-- perform a meth ns f rq = undefined -- do
509-- perform a meth ns f rq = undefined -- do
501 -- m <- ask
510 -- m <- ask
502− -- x <- lift . try $ Client.http m (clientRequest meth a rq)
511+ -- x <- lift . try $ Client.http m (client meth a rq)
503 -- either (failure . TransportError) success x
512 -- either (failure . TransportError) success x
505 -- failure = pure . Left
514 -- failure = pure . Left
@@ -542,5 +551,30 @@ buildText = Build.fromText . toText
542buildSymbol :: forall n proxy. KnownSymbol n => proxy n -> Builder
551buildSymbol :: forall n proxy. KnownSymbol n => proxy n -> Builder
543buildSymbol = Build.fromString . symbolVal
552buildSymbol = Build.fromString . symbolVal
545−textSymbol :: forall n proxy. KnownSymbol n => proxy n -> Text
554+byteSymbol :: forall n proxy. KnownSymbol n => proxy n -> ByteString
546−textSymbol = Text.pack . symbolVal
555+byteSymbol = BS8.pack . symbolVal
557+-- | An integral value representing seconds.
558+newtype Seconds = Seconds Int
574+seconds :: Seconds -> Int
579+microseconds :: Seconds -> Int
580+microseconds = (1000000 *) . seconds