@@ -7,6 +7,7 @@
77
88module Servant.Server where
99
10+import Data.Monoid
1011import Data.Proxy
1112import Network.HTTP.Types
1213import Network.Wai
@@ -19,17 +20,62 @@ serve p server = toApplication (route p server)
1920
2021toApplication :: RoutingApplication -> Application
2122toApplication ra request respond = do
22− ra request routingRespond
23+ ra request (routingRespond . routeResult)
2324 where
24− routingRespond :: Maybe Response -> IO ResponseReceived
25− routingRespond Nothing =
25+ routingRespond :: Either RouteMismatch Response -> IO ResponseReceived
26+ routingRespond (Left NotFound) =
2627 respond $ responseLBS notFound404 [] "not found"
27− routingRespond (Just response) =
28+ routingRespond (Left WrongMethod) =
29+ respond $ responseLBS methodNotAllowed405 [] "method not allowed"
30+ routingRespond (Right response) =
2831 respond response
2932
33+-- * Route mismatch
34+data RouteMismatch =
35+ NotFound -- ^ the usual "not found" error
36+ | WrongMethod -- ^ a more informative "you just got the HTTP method wrong" error
37+ deriving (Eq, Show)
38+
39+-- |
40+-- @
41+-- 'NotFound' <> x = x
42+-- 'WrongMethod' <> _ = 'WrongMethod'
43+-- @
44+instance Monoid RouteMismatch where
45+ mempty = NotFound
46+
47+ NotFound `mappend` x = x
48+ WrongMethod `mappend` _ = WrongMethod
49+
50+-- | A wrapper around @'Either' 'RouteMismatch' a@.
51+newtype RouteResult a =
52+ RR { routeResult :: Either RouteMismatch a }
53+ deriving (Eq, Show)
54+
55+failWith :: RouteMismatch -> RouteResult a
56+failWith = RR . Left
57+
58+succeedWith :: a -> RouteResult a
59+succeedWith = RR . Right
60+
61+isMismatch :: RouteResult a -> Bool
62+isMismatch (RR (Left _)) = True
63+isMismatch _ = False
64+
65+-- | If we get a `Right`, it has precedence over everything else.
66+--
67+-- This in particular means that if we could get several 'Right's,
68+-- only the first we encounter would be taken into account.
69+instance Monoid (RouteResult a) where
70+ mempty = RR $ Left mempty
71+
72+ RR (Left x) `mappend` RR (Left y) = RR $ Left (x <> y)
73+ RR (Left _) `mappend` RR (Right y) = RR $ Right y
74+ r `mappend` _ = r
75+
3076type RoutingApplication =
3177 Request -- ^ the request, the field 'pathInfo' may be modified by url routing
32− -> (Maybe Response -> IO ResponseReceived) -> IO ResponseReceived
78+ -> (RouteResult Response -> IO ResponseReceived) -> IO ResponseReceived
3379
3480class HasServer layout where
3581 type Server layout :: *