packages feed

servant-foreign 0.15.3 → 0.15.4

raw patch · 5 files changed

+180/−72 lines, 5 filesdep ~basedep ~base-compatdep ~hspec

Dependency ranges changed: base, base-compat, hspec, lens, servant, text

Files

CHANGELOG.md view
@@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-foreign/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.15.4+------++### Significant changes++- Documentation improvements.++### Other changes++- Support GHC-9.0.1.+- Bump `lens` and `hspec` dependencies.+ 0.15.3 ------ 
servant-foreign.cabal view
@@ -1,6 +1,6 @@ cabal-version:       >=1.10 name:                servant-foreign-version:             0.15.3+version:             0.15.4  synopsis:            Helpers for generating clients for servant APIs in any programming language category:            Servant, Web@@ -21,7 +21,7 @@ maintainer:          haskell-servant-maintainers@googlegroups.com copyright:           2015-2019 Servant Contributors build-type:          Simple-tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2+tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1  extra-source-files:   CHANGELOG.md@@ -41,7 +41,7 @@   --   -- note: mtl lower bound is so low because of GHC-7.8   build-depends:-      base        >= 4.9     && < 4.15+      base        >= 4.9     && < 4.16     , text        >= 1.2.3.0 && < 1.3    -- Servant dependencies@@ -52,7 +52,7 @@   -- Here can be exceptions if we really need features from the newer versions.   build-depends:       base-compat >= 0.10.5  && < 0.12-    , lens        >= 4.17    && < 4.20+    , lens        >= 4.17    && < 5.1     , http-types  >= 0.12.2  && < 0.13    hs-source-dirs:      src@@ -74,8 +74,7 @@    -- Additional dependencies   build-depends:-    hspec >= 2.6.0 && <2.8-+    hspec >= 2.6.0 && <2.9   build-tool-depends:-    hspec-discover:hspec-discover >=2.6.0 && <2.8+    hspec-discover:hspec-discover >=2.6.0 && <2.9   default-language:  Haskell2010
src/Servant/Foreign.hs view
@@ -1,20 +1,32 @@ -- | Generalizes all the data needed to make code generation work with -- arbitrary programming languages.+--+-- See documentation of 'HasForeignType' for a simple example. 'listFromAPI' returns a list of all your endpoints and their foreign types, given a mapping from Haskell types to foreign types (conventionally called `ftypes` below). module Servant.Foreign-  ( ArgType(..)-  , HeaderArg(..)-  , QueryArg(..)+  (+  -- * Main API+    listFromAPI   , Req(..)-  , ReqBodyContentType(..)-  , Segment(..)-  , SegmentType(..)+  , defReq+  , HasForeignType(..)+  , GenerateList(..)+  , HasForeign(..)+  , NoTypes+  -- * Subtypes of 'Req'   , Url(..)-    -- aliases   , Path+  , Segment(..)+  , SegmentType(..)+  , isCapture+  , captureArg+  , QueryArg(..)+  , ArgType(..)+  , HeaderArg(..)   , Arg(..)   , FunctionName(..)+  , ReqBodyContentType(..)   , PathSegment(..)-    -- lenses+    -- * Lenses   , argName   , argType   , argPath@@ -30,7 +42,7 @@   , queryArgName   , queryArgType   , headerArg-    -- prisms+    -- * Prisms   , _PathSegment   , _HeaderArg   , _ReplaceHeaderArg@@ -39,16 +51,7 @@   , _Normal   , _Flag   , _List-    -- rest of it-  , HasForeign(..)-  , HasForeignType(..)-  , GenerateList(..)-  , NoTypes-  , captureArg-  , isCapture-  , defReq-  , listFromAPI-    -- re-exports+    -- * Re-exports   , module Servant.API   , module Servant.Foreign.Inflections   ) where
src/Servant/Foreign/Inflections.hs view
@@ -20,20 +20,31 @@                  (head, tail) import           Servant.Foreign.Internal +-- | Simply concat each part of the FunctionName together.+--+-- @[ "get", "documents", "by", "id" ] → "getdocumentsbyid"@+concatCase :: FunctionName -> Text+concatCase = view concatCaseL+ concatCaseL :: Getter FunctionName Text concatCaseL = _FunctionName . to mconcat --- | Function name builder that simply concat each part together-concatCase :: FunctionName -> Text-concatCase = view concatCaseL+-- | Use the snake_case convention.+-- Each part is separated by a single underscore character.+--+-- @[ "get", "documents", "by", "id" ] → "get_documents_by_id"@+snakeCase :: FunctionName -> Text+snakeCase = view snakeCaseL  snakeCaseL :: Getter FunctionName Text snakeCaseL = _FunctionName . to (intercalate "_") --- | Function name builder using the snake_case convention.--- each part is separated by a single underscore character.-snakeCase :: FunctionName -> Text-snakeCase = view snakeCaseL+-- | Use the camelCase convention.+-- The first part is lower case, every other part starts with an upper case character.+--+-- @[ "get", "documents", "by", "id" ] → "getDocumentsById"@+camelCase :: FunctionName -> Text+camelCase = view camelCaseL  camelCaseL :: Getter FunctionName Text camelCaseL = _FunctionName . to convert@@ -42,8 +53,3 @@     convert (p:ps) = mconcat $ p : map capitalize ps     capitalize ""   = ""     capitalize name = C.toUpper (head name) `cons` tail name---- | Function name builder using the CamelCase convention.--- each part begins with an upper case character.-camelCase :: FunctionName -> Text-camelCase = view camelCaseL
src/Servant/Foreign/Internal.hs view
@@ -13,8 +13,6 @@ {-# LANGUAGE TypeOperators              #-} {-# LANGUAGE UndecidableInstances       #-} --- | Generalizes all the data needed to make code generation work with--- arbitrary programming languages. module Servant.Foreign.Internal where  import           Prelude ()@@ -40,55 +38,75 @@                  (RequiredArgument) import           Servant.API.TypeLevel +-- | Canonical name of the endpoint, can be used to generate a function name.+--+-- You can use the functions in "Servant.Foreign.Inflections", like 'Servant.Foreign.Inflections.camelCase' to transform to `Text`. newtype FunctionName = FunctionName { unFunctionName :: [Text] }   deriving (Data, Show, Eq, Semigroup, Monoid, Typeable)  makePrisms ''FunctionName +-- | See documentation of 'Arg' newtype PathSegment = PathSegment { unPathSegment :: Text }   deriving (Data, Show, Eq, IsString, Semigroup, Monoid, Typeable)  makePrisms ''PathSegment -data Arg f = Arg+-- | Maps a name to the foreign type that belongs to the annotated value.+--+-- Used for header args, query args, and capture args.+data Arg ftype = Arg   { _argName :: PathSegment-  , _argType :: f }+  -- ^ The name to be captured.+  --+  -- Only for capture args it really denotes a path segment.+  , _argType :: ftype+  -- ^ Foreign type the associated value will have+  }   deriving (Data, Eq, Show, Typeable)  makeLenses ''Arg -argPath :: Getter (Arg f) Text+argPath :: Getter (Arg ftype) Text argPath = argName . _PathSegment -data SegmentType f+data SegmentType ftype   = Static PathSegment-    -- ^ a static path segment. like "/foo"-  | Cap (Arg f)-    -- ^ a capture. like "/:userid"+    -- ^ Static path segment.+    --+    -- @"foo\/bar\/baz"@+    --+    -- contains the static segments @"foo"@, @"bar"@ and @"baz"@.+  | Cap (Arg ftype)+    -- ^ A capture.+    --+    -- @"user\/{userid}\/name"@+    --+    -- would capture the arg @userid@ with type @ftype@.   deriving (Data, Eq, Show, Typeable)  makePrisms ''SegmentType -newtype Segment f = Segment { unSegment :: SegmentType f }+-- | A part of the Url’s path.+newtype Segment ftype = Segment { unSegment :: SegmentType ftype }   deriving (Data, Eq, Show, Typeable)  makePrisms ''Segment -isCapture :: Segment f -> Bool+-- | Whether a segment is a 'Cap'.+isCapture :: Segment ftype -> Bool isCapture (Segment (Cap _)) = True isCapture                _  = False -captureArg :: Segment f -> Arg f+-- | Crashing Arg extraction from segment, TODO: remove+captureArg :: Segment ftype -> Arg ftype captureArg (Segment (Cap s)) = s captureArg                 _ = error "captureArg called on non capture" -type Path f = [Segment f]--newtype Frag f = Frag { unFragment :: Arg f }-  deriving (Data, Eq, Show, Typeable)--makePrisms ''Frag+-- TODO: remove, unnecessary indirection+type Path ftype = [Segment ftype] +-- | Type of a 'QueryArg'. data ArgType   = Normal   | Flag@@ -97,18 +115,41 @@  makePrisms ''ArgType -data QueryArg f = QueryArg-  { _queryArgName :: Arg f+-- | Url Query argument.+--+-- Urls can contain query arguments, which is a list of key-value pairs.+-- In a typical url, query arguments look like this:+--+-- @?foo=bar&alist[]=el1&alist[]=el2&aflag@+--+-- Each pair can be+--+-- * @?foo=bar@: a plain key-val pair, either optional or required ('QueryParam')+-- * @?aflag@: a flag (no value, implicitly Bool with default `false` if it’s missing) ('QueryFlag')+-- * @?alist[]=el1&alist[]=el2@: list of values ('QueryParams')+--+-- @_queryArgType@ will be set accordingly.+--+-- For the plain key-val pairs ('QueryParam'), @_queryArgName@’s @ftype@ will be wrapped in a @Maybe@ if the argument is optional.+data QueryArg ftype = QueryArg+  { _queryArgName :: Arg ftype+  -- ^ Name and foreign type of the argument. Will be wrapped in `Maybe` if the query is optional and in a `[]` if the query is a list   , _queryArgType :: ArgType+  -- ^ one of normal/plain, list or flag   }   deriving (Data, Eq, Show, Typeable)  makeLenses ''QueryArg -data HeaderArg f = HeaderArg-  { _headerArg :: Arg f }+data HeaderArg ftype =+  -- | The name of the header and the foreign type of its value.+  HeaderArg+  { _headerArg :: Arg ftype }+  -- | Unused, will never be set.+  --+  -- TODO: remove   | ReplaceHeaderArg-  { _headerArg     :: Arg f+  { _headerArg     :: Arg ftype   , _headerPattern :: Text   }   deriving (Data, Eq, Show, Typeable)@@ -117,29 +158,71 @@  makePrisms ''HeaderArg -data Url f = Url-  { _path     :: Path f-  , _queryStr :: [QueryArg f]-  , _frag     :: Maybe f+-- | Full endpoint url, with all captures and parameters+data Url ftype = Url+  { _path     :: Path ftype+  -- ^ Url path, list of either static segments or captures+  --+  -- @"foo\/{id}\/bar"@+  , _queryStr :: [QueryArg ftype]+  -- ^ List of query args+  --+  -- @"?foo=bar&a=b"@+  , _frag     :: Maybe ftype+  -- ^ Url fragment.+  --+  -- Not sent to the HTTP server, so only useful for frontend matters (e.g. inter-page linking).+  --+  -- @#fragmentText@   }   deriving (Data, Eq, Show, Typeable) -defUrl :: Url f+defUrl :: Url ftype defUrl = Url [] [] Nothing  makeLenses ''Url +-- | See documentation of '_reqBodyContentType' data ReqBodyContentType = ReqBodyJSON | ReqBodyMultipart   deriving (Data, Eq, Show, Read) -data Req f = Req-  { _reqUrl             :: Url f+-- | Full description of an endpoint in your API, generated by 'listFromAPI'. It should give you all the information needed to generate foreign language bindings.+--+-- Every field containing @ftype@ will use the foreign type mapping specified via 'HasForeignType' (see its docstring on how to set that up).+--+-- See https://docs.servant.dev/en/stable/tutorial/ApiType.html for accessible documentation of the possible content of an endpoint.+data Req ftype = Req+  { _reqUrl             :: Url ftype+  -- ^ Full list of URL segments, including captures   , _reqMethod          :: HTTP.Method-  , _reqHeaders         :: [HeaderArg f]-  , _reqBody            :: Maybe f-  , _reqReturnType      :: Maybe f+  -- ^ @\"GET\"@\/@\"POST\"@\/@\"PUT\"@\/…+  , _reqHeaders         :: [HeaderArg ftype]+  -- ^ Headers required by this endpoint, with their type+  , _reqBody            :: Maybe ftype+  -- ^ Foreign type of the expected request body ('ReqBody'), if any+  , _reqReturnType      :: Maybe ftype+  -- ^ The foreign type of the response, if any   , _reqFuncName        :: FunctionName+  -- ^ The URL segments rendered in a way that they can be easily concatenated into a canonical function name   , _reqBodyContentType :: ReqBodyContentType+  -- ^ The content type the request body is transferred as.+  --+  -- This is a severe limitation of @servant-foreign@ currently,+  -- as we only allow the content type to be `JSON`+  -- no user-defined content types. ('ReqBodyMultipart' is not+  -- actually implemented.)+  --+  -- Thus, any routes looking like this will work:+  --+  -- @"foo" :> Get '[JSON] Foo@+  --+  -- while routes like+  --+  -- @"foo" :> Get '[MyFancyContentType] Foo@+  --+  -- will fail with an error like+  --+  -- @• JSON expected in list '[MyFancyContentType]@   }   deriving (Data, Eq, Show, Typeable) @@ -183,11 +266,16 @@ class HasForeignType lang ftype a where   typeFor :: Proxy lang -> Proxy ftype -> Proxy a -> ftype +-- | The language definition without any foreign types. It can be used for dynamic languages which do not /do/ type annotations. data NoTypes -instance HasForeignType NoTypes NoContent ftype where+-- | Use if the foreign language does not have any types.+instance HasForeignType NoTypes NoContent a where   typeFor _ _ _ = NoContent +-- | Implementation of the Servant framework types.+--+-- Relevant instances: Everything containing 'HasForeignType'. class HasForeign lang ftype (api :: *) where   type Foreign ftype api :: *   foreignFor :: Proxy lang -> Proxy ftype -> Proxy api -> Req ftype -> Foreign ftype api