Skip to content

Commit

Permalink
Merge pull request #1641 from amesgen/custom-error-for-named-routes-n…
Browse files Browse the repository at this point in the history
…o-generic

Better type errors for `NamedRoutes` without `Generic` instance
  • Loading branch information
gdeest committed Mar 9, 2023
2 parents a082794 + 420d633 commit 38f519a
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 1 deletion.
1 change: 1 addition & 0 deletions servant-client-core/src/Servant/Client/Core/HasClient.hs
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,7 @@ instance
( forall n. GClient api n
, HasClient m (ToServantApi api)
, RunClient m
, ErrorIfNoGeneric api
)
=> HasClient m (NamedRoutes api) where
type Client m (NamedRoutes api) = api (AsClientT m)
Expand Down
6 changes: 5 additions & 1 deletion servant-docs/src/Servant/Docs/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import qualified GHC.Generics as G
import GHC.TypeLits
import Servant.API
import Servant.API.ContentTypes
import Servant.API.TypeErrors
import Servant.API.TypeLevel
import Servant.API.Generic

Expand Down Expand Up @@ -1154,7 +1155,10 @@ instance (ToAuthInfo (BasicAuth realm usr), HasDocs api) => HasDocs (BasicAuth r
authProxy = Proxy :: Proxy (BasicAuth realm usr)
action' = over authInfo (|> toAuthInfo authProxy) action

instance HasDocs (ToServantApi api) => HasDocs (NamedRoutes api) where
instance
( HasDocs (ToServantApi api)
, ErrorIfNoGeneric api
) => HasDocs (NamedRoutes api) where
docsFor Proxy = docsFor (Proxy :: Proxy (ToServantApi api))

-- ToSample instances for simple types
Expand Down
1 change: 1 addition & 0 deletions servant-server/src/Servant/Server/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,7 @@ instance
( HasServer (ToServantApi api) context
, forall m. Generic (api (AsServerT m))
, forall m. GServer api m
, ErrorIfNoGeneric api
) => HasServer (NamedRoutes api) context where

type ServerT (NamedRoutes api) m = api (AsServerT m)
Expand Down
19 changes: 19 additions & 0 deletions servant/src/Servant/API/TypeErrors.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
Expand All @@ -12,9 +13,11 @@ module Servant.API.TypeErrors (
PartialApplication,
NoInstanceFor,
NoInstanceForSub,
ErrorIfNoGeneric,
) where

import Data.Kind
import GHC.Generics (Generic(..))
import GHC.TypeLits

-- | No instance exists for @tycls (expr :> ...)@ because
Expand All @@ -38,3 +41,19 @@ type Arity (ty :: k) = Arity' k
type family Arity' (ty :: k) :: Nat where
Arity' (_ -> ty) = 1 + Arity' ty
Arity' _ = 0

-- see https://blog.csongor.co.uk/report-stuck-families/
type ErrorIfNoGeneric routes = Break (NoGeneric routes :: Type) (Rep (routes ()))

data T1 a

type family Break err a :: Constraint where
Break _ T1 = ((), ())
Break _ a = ()

type family NoGeneric (routes :: Type -> Type) where
NoGeneric routes = TypeError
( 'Text "Named routes require a "
':<>: 'ShowType Generic ':<>: 'Text " instance for "
':<>: 'ShowType routes
)
1 change: 1 addition & 0 deletions servant/src/Servant/Links.hs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ instance GLinkConstraints routes a => GLink routes a where
instance
( HasLink (ToServantApi routes)
, forall a. GLink routes a
, ErrorIfNoGeneric routes
) => HasLink (NamedRoutes routes) where

type MkLink (NamedRoutes routes) a = routes (AsLink a)
Expand Down

0 comments on commit 38f519a

Please sign in to comment.