servant-cassava 0.9 → 0.10
raw patch · 3 files changed
+112/−23 lines, 3 filesdep +servant-cassavadep +servant-serverdep +waidep ~basedep ~base-compatdep ~cassavaPVP ok
version bump matches the API change (PVP)
Dependencies added: servant-cassava, servant-server, wai, warp
Dependency ranges changed: base, base-compat, cassava, servant
API changes (from Hackage documentation)
- Servant.CSV.Cassava: class DecodeOpts a
- Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromNamedRecord a, Servant.CSV.Cassava.DecodeOpts opt) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' 'Data.Csv.Types.HasHeader opt) (Data.Csv.Types.Header, Data.Vector.Vector a)
- Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromNamedRecord a, Servant.CSV.Cassava.DecodeOpts opt) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' 'Data.Csv.Types.HasHeader opt) (Data.Csv.Types.Header, [a])
- Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromRecord a, Servant.CSV.Cassava.DecodeOpts opt, Servant.CSV.Cassava.SHasHeaderI hasHeader) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' hasHeader opt) (Data.Vector.Vector a)
- Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromRecord a, Servant.CSV.Cassava.DecodeOpts opt, Servant.CSV.Cassava.SHasHeaderI hasHeader) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' hasHeader opt) [a]
- Servant.CSV.Cassava: instance Servant.API.ContentTypes.Accept (Servant.CSV.Cassava.CSV' hasHeader opt)
- Servant.CSV.Cassava: instance Servant.CSV.Cassava.DecodeOpts Servant.CSV.Cassava.DefaultOpts
+ Servant.CSV.Cassava: csvContentType :: EncodeOpts opt => Proxy opt -> MediaType
+ Servant.CSV.Cassava: data TabSeparatedOpts
+ Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromNamedRecord a, Servant.CSV.Cassava.EncodeOpts opt) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' 'Data.Csv.Types.HasHeader opt) (Data.Csv.Types.Header, Data.Vector.Vector a)
+ Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromNamedRecord a, Servant.CSV.Cassava.EncodeOpts opt) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' 'Data.Csv.Types.HasHeader opt) (Data.Csv.Types.Header, [a])
+ Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromRecord a, Servant.CSV.Cassava.EncodeOpts opt, Servant.CSV.Cassava.SHasHeaderI hasHeader) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' hasHeader opt) (Data.Vector.Vector a)
+ Servant.CSV.Cassava: instance (Data.Csv.Conversion.FromRecord a, Servant.CSV.Cassava.EncodeOpts opt, Servant.CSV.Cassava.SHasHeaderI hasHeader) => Servant.API.ContentTypes.MimeUnrender (Servant.CSV.Cassava.CSV' hasHeader opt) [a]
+ Servant.CSV.Cassava: instance GHC.Generics.Generic Servant.CSV.Cassava.TabSeparatedOpts
+ Servant.CSV.Cassava: instance Servant.CSV.Cassava.EncodeOpts Servant.CSV.Cassava.TabSeparatedOpts
+ Servant.CSV.Cassava: instance Servant.CSV.Cassava.EncodeOpts opt => Servant.API.ContentTypes.Accept (Servant.CSV.Cassava.CSV' hasHeader opt)
- Servant.CSV.Cassava: class EncodeOpts opt
+ Servant.CSV.Cassava: class EncodeOpts opt where decodeOpts p = DecodeOptions {decDelimiter = encDelimiter e} where e = encodeOpts p csvContentType p = case encDelimiter (encodeOpts p) of { 9 -> "text" // "tab-separated-values" /: ("charset", "utf-8") _ -> "text" // "csv" /: ("charset", "utf-8") }
- Servant.CSV.Cassava: decodeOpts :: DecodeOpts a => Proxy a -> DecodeOptions
+ Servant.CSV.Cassava: decodeOpts :: EncodeOpts opt => Proxy opt -> DecodeOptions
Files
- example/Main.hs +51/−0
- servant-cassava.cabal +29/−5
- src/Servant/CSV/Cassava.hs +32/−18
+ example/Main.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE TypeOperators #-}+module Main (main, Foo (..)) where++import Prelude ()+import Prelude.Compat++import Data.Csv (DefaultOrdered, FromRecord, ToNamedRecord, ToRecord)+import Data.Maybe (fromMaybe)+import GHC.Generics (Generic)+import Network.Wai (Application)+import Servant+import Servant.CSV.Cassava+import System.Environment (getArgs, lookupEnv)+import Text.Read (readMaybe)++import qualified Network.Wai.Handler.Warp as Warp++data Foo = Foo+ { fooName :: String+ , fooNum :: Int+ }+ deriving (Eq, Show, Generic)++instance DefaultOrdered Foo+instance FromRecord Foo+instance ToNamedRecord Foo+instance ToRecord Foo++type API = "post" :> ReqBody '[CSV] [Foo] :> Post '[CSV] [Foo]+ :<|> "csv" :> Get '[CSV] [Foo]+ :<|> "tsv" :> Get '[CSV' 'NoHeader TabSeparatedOpts] [Foo]++api :: Proxy API+api = Proxy++server :: Server API+server = pure :<|> pure [Foo "Csv!" 42] :<|> pure [Foo "Tsv!" 7]++app :: Application+app = serve api server++main :: IO ()+main = do+ args <- getArgs+ case args of+ ("run":_) -> do+ port <- fromMaybe 8000 . (>>= readMaybe) <$> lookupEnv "PORT"+ Warp.run port app+ _ -> putStrLn "To run, pass run argument"
servant-cassava.cabal view
@@ -1,5 +1,5 @@ name: servant-cassava-version: 0.9+version: 0.10 synopsis: Servant CSV content-type for cassava description: Servant CSV content-type for cassava. homepage: http://haskell-servant.readthedocs.org/@@ -8,10 +8,15 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2015-2016 Servant Contributors-category: Web+category: Web, Servant, CSV build-type: Simple cabal-version: >=1.10 bug-reports: http://github.com/haskell-servant/servant-cassava/issues+tested-with:+ GHC==7.8.4,+ GHC==7.10.3,+ GHC==8.0.2,+ GHC==8.2.1 source-repository head type: git@@ -19,13 +24,32 @@ library exposed-modules: Servant.CSV.Cassava- build-depends: base >=4.6 && <5+ build-depends: base >=4.7 && <5 , base-compat >=0.9.1 && <0.10 , bytestring- , cassava >0.4 && <0.5- , servant >=0.7 && <0.12+ , cassava >=0.4.3 && <0.6+ , servant >=0.7 && <0.13 , http-media , vector hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall++test-suite example+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ example+ ghc-options: -Wall+ build-depends:+ base+ , base-compat+ , bytestring+ , http-media+ , servant+ , cassava+ , servant-cassava+ , servant-server >=0.4.4.5 && <0.13+ , wai >=3.0.3.0 && <3.3+ , warp >=3.0.13.1 && <3.3+ default-language: Haskell2010
src/Servant/CSV/Cassava.hs view
@@ -56,13 +56,16 @@ lowerSHasHeader SHasHeader = HasHeader lowerSHasHeader SNoHeader = NoHeader --- | Default options, instances providing 'defaultDecodeOptions' and 'defaultEncodeOptions'.+-- | Default options, instances providing 'defaultDecodeOptions' and 'defaultEncodeOptions', and content type @text/csv;charset=utf-8@ data DefaultOpts deriving (Typeable, Generic) --- | @text/csv;charset=utf-8@-instance Accept (CSV' hasHeader opt) where- contentType _ = "text" M.// "csv" M./: ("charset", "utf-8")+-- | Options that work for tab delimited data, with content type @text/tab-separated-values;charset=utf-8@+data TabSeparatedOpts deriving (Typeable, Generic) +-- | Content type can be determined to coincide with encode opts.+instance EncodeOpts opt => Accept (CSV' hasHeader opt) where+ contentType _ = csvContentType (Proxy :: Proxy opt)+ -- * Encoding -- ** Instances@@ -114,11 +117,24 @@ where opts = encodeOpts (Proxy :: Proxy opt) --- ** Encode Options+-- ** Encode/Decode Options class EncodeOpts opt where encodeOpts :: Proxy opt -> EncodeOptions + decodeOpts :: Proxy opt -> DecodeOptions+ decodeOpts p = DecodeOptions+ { decDelimiter = encDelimiter e+ }+ where+ e = encodeOpts p++ csvContentType :: Proxy opt -> M.MediaType+ csvContentType p = case encDelimiter (encodeOpts p) of+ -- ord '\t' = 9+ 9 -> "text" M.// "tab-separated-values" M./: ("charset", "utf-8")+ _ -> "text" M.// "csv" M./: ("charset", "utf-8")+ encodeOpts' :: forall opt hasHeader. (EncodeOpts opt, SHasHeaderI hasHeader) => Proxy opt -> Proxy hasHeader -> EncodeOptions@@ -128,43 +144,41 @@ instance EncodeOpts DefaultOpts where encodeOpts _ = defaultEncodeOptions+ decodeOpts _ = defaultDecodeOptions +instance EncodeOpts TabSeparatedOpts where+ -- ord '\t' = 9+ encodeOpts _ = defaultEncodeOptions { encDelimiter = 9 }+ decodeOpts _ = defaultDecodeOptions { decDelimiter = 9 }++ -- * Decoding -- ** Instances -- | Decode with 'decodeByNameWith'.-instance ( FromNamedRecord a, DecodeOpts opt+instance ( FromNamedRecord a, EncodeOpts opt ) => MimeUnrender (CSV' 'HasHeader opt) (Header, [a]) where mimeUnrender _ bs = fmap toList <$> decodeByNameWith (decodeOpts p) bs where p = Proxy :: Proxy opt -- | Decode with 'decodeWith'.-instance ( FromRecord a, DecodeOpts opt, SHasHeaderI hasHeader+instance ( FromRecord a, EncodeOpts opt, SHasHeaderI hasHeader ) => MimeUnrender (CSV' hasHeader opt) [a] where mimeUnrender _ = fmap toList . decodeWith (decodeOpts p) (lowerSHasHeader sh) where p = Proxy :: Proxy opt sh = shasheader :: SHasHeader hasHeader -instance ( FromNamedRecord a, DecodeOpts opt+instance ( FromNamedRecord a, EncodeOpts opt ) => MimeUnrender (CSV' 'HasHeader opt) (Header, Vector a) where mimeUnrender _ = decodeByNameWith (decodeOpts p) where p = Proxy :: Proxy opt -- | Decode with 'decodeWith'.-instance ( FromRecord a, DecodeOpts opt, SHasHeaderI hasHeader+instance ( FromRecord a, EncodeOpts opt, SHasHeaderI hasHeader ) => MimeUnrender (CSV' hasHeader opt) (Vector a) where mimeUnrender _ = decodeWith (decodeOpts p) (lowerSHasHeader sh) where p = Proxy :: Proxy opt sh = shasheader :: SHasHeader hasHeader----- ** Decode Options--class DecodeOpts a where- decodeOpts :: Proxy a -> DecodeOptions--instance DecodeOpts DefaultOpts where- decodeOpts _ = defaultDecodeOptions