1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2 {-# LANGUAGE StandaloneDeriving #-}
3 {-# LANGUAGE UndecidableInstances #-}
4 {-# OPTIONS_GHC -Wno-orphans #-}
6 module Literate.Web.Semantics.Server where
8 -- import Data.Text.Lazy qualified as TextL
9 -- import Data.Text.Lazy.Encoding qualified as TextL
10 -- import Network.Wai qualified as Wai
11 -- import Prelude (undefined)
12 -- import Data.Text.Encoding qualified as Text
13 import Control.Applicative (Applicative (..))
14 import Control.Monad (Monad (..))
15 import Control.Monad.Classes qualified as MC
16 import Control.Monad.Trans.Class qualified as MT
17 import Control.Monad.Trans.Except qualified as MT
18 import Control.Monad.Trans.Reader qualified as MT
19 import Control.Monad.Trans.State qualified as MT
20 import Data.Bifunctor (first)
22 import Data.ByteString.Lazy qualified as BSL
23 import Data.Either (Either (..))
24 import Data.Eq (Eq (..))
25 import Data.Foldable (null)
26 import Data.Function (($), (.))
27 import Data.Functor (Functor, (<$>))
28 import Data.List qualified as List
29 import Data.Maybe (Maybe (..))
31 import Data.Semigroup (Semigroup (..))
33 import Data.Set qualified as Set
34 import Data.Text qualified as Text
35 import Data.Text.Encoding.Error qualified as Text
36 import Symantic.Syntaxes (Iso (..))
38 import Text.Show (Show (..))
42 import Literate.Web.Syntaxes
44 -- import Literate.Web.Types.MIME
45 import Literate.Web.Types.URL
47 -- * The 'Server' interpreter
49 -- | A very very basic parser.
50 newtype Server err m a = Server
56 (MT.ExceptT (ServerError err) m)
60 deriving (Functor, Applicative, Monad)
63 data Request = Request
64 { requestPathSegments :: [PathSegment]
65 , requestBody :: BSL.ByteString
69 data Servers err a = Servers
70 { serversPath :: [PathSegment] -> a
71 , serversMethod :: [PathSegment] -> Bool
72 --, serversBasicAuth ::
73 , serversAccept :: Bool
74 , serversContentType :: Bool
75 , serversQuery :: Bool
76 , serversHeader :: Bool
77 , serversBody :: BSL.ByteString -> a
81 data ServerState = ServerState
82 { serverStatePathSegments :: [PathSegment]
85 decode :: (Monad m) => Server err m a -> Request -> m (Either (ServerError err) a)
86 decode (Server dec) req =
87 MT.runExceptT (MT.runStateT (MT.runReaderT dec req) st) >>= \case
88 Left err -> return $ Left err
89 Right (a, ServerState{..})
90 | null serverStatePathSegments -> return $ Right a
91 | otherwise -> return $ Left $ ServerErrorPathLeftover serverStatePathSegments
95 { serverStatePathSegments = requestPathSegments req
99 = -- 1st checks, 404 error
100 ServerErrorPathMismatch
101 { expectedPathSegments :: Set PathSegment
102 , gotPathSegment :: PathSegment
104 | ServerErrorPathMissing
105 | ServerErrorPathLeftover [PathSegment]
106 | -- 2nd check, 405 error
108 | -- 3rd check, 401 or 403 error
110 | -- 4th check, 406 error
112 | -- 5th check, 415 error
113 ServerErrorContentType
114 | -- 6th check, 400 error
116 | -- 7th check, 400 error
118 | -- 8th check, 400 error
119 ServerErrorUnicode Text.UnicodeException
120 | -- 9th check, custom
121 ServerErrorParser err
122 deriving (Eq, Ord, Show)
123 deriving instance Ord Text.UnicodeException
125 instance (Monad m) => IsoFunctor (Server err m) where
126 (<%>) Iso{..} = (a2b <$>)
127 instance (Monad m) => ProductFunctor (Server err m) where
131 instance (Monad m, Ord err) => SumFunctor (Server err m) where
132 Server x <+> Server y = Server $
133 MT.ReaderT $ \env -> MT.StateT $ \st -> do
134 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT x env) st)) >>= \case
135 Right (a, st') -> return (Left a, st')
137 -- TODO: better error merging
138 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT y env) st)) >>= \case
139 Right (b, st') -> return (Right b, st')
140 Left yErr -> MT.throwE (min xErr yErr)
142 -- instance Endable (Server err m) where
144 -- MT.lift (MT.gets serverStatePathSegments) >>= \case
146 -- lo -> MT.lift $ MT.lift $ MT.throwE $ ServerErrorPathLeftover lo
147 instance (Monad m) => Repeatable (Server err m) where
148 many0 (Server x) = Server (MT.ReaderT (MT.StateT . go))
151 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT x env) st)) >>= \case
152 Left _err -> return ([], st) -- always backtrack
153 Right (a, st') -> first (a :) <$> go env st'
154 many1 x = (:) <$> x <*> many0 x
155 instance (Monad m) => Optionable (Server err m) where
156 optional (Server x) = Server $
157 MT.ReaderT $ \env -> MT.StateT $ \st -> do
158 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT x env) st)) >>= \case
159 Left{} -> return (Nothing, st)
160 Right (a, st') -> return (Just a, st')
161 instance (Monad m) => PathSegmentable (Server err m) where
162 pathSegment expectedPathSegment = Server $ do
163 ps <- MT.lift (MT.gets serverStatePathSegments)
165 [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
166 gotPathSegment : nextPathSegments
167 | expectedPathSegment /= gotPathSegment ->
168 MT.lift $ MT.lift $ MT.throwE ServerErrorPathMismatch{expectedPathSegments = Set.singleton expectedPathSegment, ..}
170 MT.local (\req -> req{requestPathSegments = requestPathSegments req <> [gotPathSegment]}) $
173 \st -> st{serverStatePathSegments = nextPathSegments}
175 -- choosePathSegment expectedPathSegments = Server $ do
176 -- ps <- MT.lift (MT.gets serverStatePathSegments)
178 -- [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
179 -- gotPathSegment : nextPathSegments
180 -- | gotPathSegment `Set.notMember` expectedPathSegments ->
181 -- MT.lift $ MT.lift $ MT.throwE ServerErrorPathMismatch{expectedPathSegments, ..}
183 -- MT.local (\req -> req{requestPathSegments = requestPathSegments req <> [gotPathSegment]}) $
184 -- MT.lift $ MT.modify' $ \st -> st{serverStatePathSegments = nextPathSegments}
185 -- return gotPathSegment
187 instance ContentTypeable PlainText BSL.ByteString (Server err m) where
188 contentType = Server do
189 Request{..} <- MT.ask
191 instance ContentTypeable PlainText Text.Text (Server err m) where
192 contentType = Server do
193 Request{..} <- MT.ask
194 case Text.decodeUtf8' (BSL.toStrict requestBody) of
196 Left err -> MT.lift $ MT.lift $ MT.throwE $ ServerErrorUnicode err
199 -- choosePathSegments = undefined
200 -- choosePathSegments expectedPathSegments = Server $ do
201 -- ps <- MT.lift MT.get
203 -- [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
204 -- gotPathSegment : nextPathSegments
205 -- | gotPathSegment `Set.member` expectedPathSegments ->
206 -- MT.lift $ MT.lift $ MT.throwE $ ServerErrorPathMismatch{expectedPathSegments, ..}
208 -- MT.local (<> [gotPathSegment]) $
209 -- MT.lift $ MT.put nextPathSegments
210 -- return gotPathSegment
211 instance (Monad m) => Capturable PathSegment (Server err m) where
212 capturePathSegment _name = Server $ do
213 ps <- MT.lift (MT.gets serverStatePathSegments)
215 [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
216 gotPathSegment : nextPathSegments ->
217 MT.local (\req -> req{requestPathSegments = requestPathSegments req <> [gotPathSegment]}) do
218 MT.lift $ MT.modify' $ \st -> st{serverStatePathSegments = nextPathSegments}
219 return gotPathSegment
221 -- choosePathSegment expectedPathSegments = Server $ do
222 -- ps <- MT.lift MT.get
224 -- [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
225 -- gotPathSegment : nextPathSegments
226 -- | gotPathSegment `Set.member` expectedPathSegments ->
227 -- MT.lift $ MT.lift $ MT.throwE $ ServerErrorPathMismatch{..}
229 -- MT.local (<> [gotPathSegment]) $
230 -- MT.lift $ MT.put nextPathSegments
231 -- return gotPathSegment
233 instance Selectable (Server err m) where
236 case Map.lookup a a2bs of
238 Server $ MT.lift $ MT.lift $ MT.throwE
239 ServerErrorPathMissing -- FIXME
242 MT.lift $ MT.lift $ MT.throwE
243 ServerErrorPathMissing -- FIXME
244 go a ((ca, x):xs) = Server $
245 MT.ReaderT $ \env -> MT.StateT $ \st -> do
246 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT (unServer x) env) st)) >>= \case
249 MT.runStateT (MT.runReaderT (unServer (choose ra xs)) env) st
251 instance (MC.MonadExec IO m) => Fileable (Server err m) where
252 type FileableConstraint (Server err m) = Parsable err
256 Request{..} <- MT.ask
263 List.intercalate "/" $
264 Text.unpack . encodePathSegment <$> requestPathSegments
265 case parse content of
267 Left err -> MT.lift $ MT.lift $ MT.throwE $ ServerErrorParser err
269 -- * Class 'Parsable'
270 class Parsable err a where
271 parse :: BSL.ByteString -> Either err a