1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2 {-# LANGUAGE StandaloneDeriving #-}
3 {-# LANGUAGE UndecidableInstances #-}
4 {-# OPTIONS_GHC -Wno-orphans #-}
6 module Literate.Web.Semantics.Server where
8 import Control.Applicative (Applicative (..))
9 import Control.Monad (Monad (..))
10 import Control.Monad.Trans.Class qualified as MT
11 import Control.Monad.Trans.Except qualified as MT
12 import Control.Monad.Trans.Reader qualified as MT
13 import Control.Monad.Trans.State qualified as MT
14 import Data.Bifunctor (first)
16 import Data.ByteString.Lazy qualified as BSL
17 import Data.Either (Either (..))
18 import Data.Eq (Eq (..))
19 import Data.Foldable (null)
20 import Data.Function (($), (.))
21 import Data.Functor (Functor, (<$>))
22 import Data.List qualified as List
23 import Data.Maybe (Maybe (..))
25 import Data.Semigroup (Semigroup (..))
27 import Data.Set qualified as Set
28 import Data.Text qualified as Text
29 import Data.Text.Encoding qualified as Text
30 import Data.Text.Encoding.Error qualified as Text
32 -- import Data.Text.Lazy qualified as TextL
33 -- import Data.Text.Lazy.Encoding qualified as TextL
34 import Symantic.Syntaxes (Iso (..), IsoFunctor ((<%>)), Optionable (..), ProductFunctor (..), SumFunctor ((<+>)))
36 import Text.Show (Show (..))
38 -- import Network.Wai qualified as Wai
40 -- import Prelude (undefined)
42 import Literate.Web.Syntaxes
43 import Literate.Web.Types.MIME
44 import Literate.Web.Types.URL
46 -- * The 'Server' interpreter
48 -- | A very very basic parser.
49 newtype Server model err a = Server
55 (MT.ExceptT (ServerError err) IO)
59 deriving (Functor, Applicative, Monad)
61 data ServerEnv model = ServerEnv
62 { serverEnvRequest :: Request
63 , serverEnvModel :: model
67 data Request = Request
68 { requestPathSegments :: [PathSegment]
69 , requestBody :: BSL.ByteString
73 data Servers err a = Servers
74 { serversPath :: [PathSegment] -> a
75 , serversMethod :: [PathSegment] -> Bool
76 --, serversBasicAuth ::
77 , serversAccept :: Bool
78 , serversContentType :: Bool
79 , serversQuery :: Bool
80 , serversHeader :: Bool
81 , serversBody :: BSL.ByteString -> a
85 data ServerState = ServerState
86 { serverStatePathSegments :: [PathSegment]
89 decode :: Server err a -> Request -> IO (Either (ServerError err) a)
90 decode (Server dec) req =
91 MT.runExceptT (MT.runStateT (MT.runReaderT dec req) st) >>= \case
92 Left err -> return $ Left err
93 Right (a, ServerState{..})
94 | null serverStatePathSegments -> return $ Right a
95 | otherwise -> return $ Left $ ServerErrorPathLeftover serverStatePathSegments
99 { serverStatePathSegments = requestPathSegments req
103 = -- 1st checks, 404 error
104 ServerErrorPathMismatch
105 { expectedPathSegments :: Set PathSegment
106 , gotPathSegment :: PathSegment
108 | ServerErrorPathMissing
109 | ServerErrorPathLeftover [PathSegment]
110 | -- 2nd check, 405 error
112 | -- 3rd check, 401 or 403 error
114 | -- 4th check, 406 error
116 | -- 5th check, 415 error
117 ServerErrorContentType
118 | -- 6th check, 400 error
120 | -- 7th check, 400 error
122 | -- 8th check, 400 error
123 ServerErrorUnicode Text.UnicodeException
124 | -- 9th check, custom
125 ServerErrorParser err
127 deriving instance Ord Text.UnicodeException
129 instance IsoFunctor (Server err) where
130 (<%>) Iso{..} = (a2b <$>)
131 instance ProductFunctor (Server err) where
135 instance SumFunctor (Server err) where
136 Server x <+> Server y = Server $
137 MT.ReaderT $ \env -> MT.StateT $ \st -> do
138 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT x env) st)) >>= \case
139 Right (a, st') -> return (Left a, st')
141 -- TODO: better error merging
142 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT y env) st)) >>= \case
143 Right (b, st') -> return (Right b, st')
144 Left err -> MT.throwE err
146 -- instance Endable (Server err) where
148 -- MT.lift (MT.gets serverStatePathSegments) >>= \case
150 -- lo -> MT.lift $ MT.lift $ MT.throwE $ ServerErrorPathLeftover lo
151 instance Repeatable (Server err) where
152 many0 (Server x) = Server (MT.ReaderT (MT.StateT . go))
155 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT x env) st)) >>= \case
156 Left _err -> return ([], st) -- always backtrack
157 Right (a, st') -> first (a :) <$> go env st'
158 many1 x = (:) <$> x <*> many0 x
159 instance Optionable (Server err) where
160 optional (Server x) = Server $
161 MT.ReaderT $ \env -> MT.StateT $ \st -> do
162 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT x env) st)) >>= \case
163 Left{} -> return (Nothing, st)
164 Right (a, st') -> return (Just a, st')
165 instance PathSegmentable (Server err) where
166 pathSegment expectedPathSegment = Server $ do
167 ps <- MT.lift (MT.gets serverStatePathSegments)
169 [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
170 gotPathSegment : nextPathSegments
171 | expectedPathSegment /= gotPathSegment ->
172 MT.lift $ MT.lift $ MT.throwE ServerErrorPathMismatch{expectedPathSegments = Set.singleton expectedPathSegment, ..}
174 MT.local (\req -> req{requestPathSegments = requestPathSegments req <> [gotPathSegment]}) $
177 \st -> st{serverStatePathSegments = nextPathSegments}
179 -- choosePathSegment expectedPathSegments = Server $ do
180 -- ps <- MT.lift (MT.gets serverStatePathSegments)
182 -- [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
183 -- gotPathSegment : nextPathSegments
184 -- | gotPathSegment `Set.notMember` expectedPathSegments ->
185 -- MT.lift $ MT.lift $ MT.throwE ServerErrorPathMismatch{expectedPathSegments, ..}
187 -- MT.local (\req -> req{requestPathSegments = requestPathSegments req <> [gotPathSegment]}) $
188 -- MT.lift $ MT.modify' $ \st -> st{serverStatePathSegments = nextPathSegments}
189 -- return gotPathSegment
191 instance ContentTypeable PlainText BSL.ByteString (Server err) where
192 contentType = Server do
193 Request{..} <- MT.ask
195 instance ContentTypeable PlainText Text.Text (Server err) where
196 contentType = Server do
197 Request{..} <- MT.ask
198 case Text.decodeUtf8' (BSL.toStrict requestBody) of
200 Left err -> MT.lift $ MT.lift $ MT.throwE $ ServerErrorUnicode err
203 -- choosePathSegments = undefined
204 -- choosePathSegments expectedPathSegments = Server $ do
205 -- ps <- MT.lift MT.get
207 -- [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
208 -- gotPathSegment : nextPathSegments
209 -- | gotPathSegment `Set.member` expectedPathSegments ->
210 -- MT.lift $ MT.lift $ MT.throwE $ ServerErrorPathMismatch{expectedPathSegments, ..}
212 -- MT.local (<> [gotPathSegment]) $
213 -- MT.lift $ MT.put nextPathSegments
214 -- return gotPathSegment
215 instance Capturable PathSegment (Server err) where
216 capturePathSegment _name = Server $ do
217 ps <- MT.lift (MT.gets serverStatePathSegments)
219 [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
220 gotPathSegment : nextPathSegments ->
221 MT.local (\req -> req{requestPathSegments = requestPathSegments req <> [gotPathSegment]}) do
222 MT.lift $ MT.modify' $ \st -> st{serverStatePathSegments = nextPathSegments}
223 return gotPathSegment
225 -- choosePathSegment expectedPathSegments = Server $ do
226 -- ps <- MT.lift MT.get
228 -- [] -> MT.lift $ MT.lift $ MT.throwE ServerErrorPathMissing
229 -- gotPathSegment : nextPathSegments
230 -- | gotPathSegment `Set.member` expectedPathSegments ->
231 -- MT.lift $ MT.lift $ MT.throwE $ ServerErrorPathMismatch{..}
233 -- MT.local (<> [gotPathSegment]) $
234 -- MT.lift $ MT.put nextPathSegments
235 -- return gotPathSegment
237 instance Selectable (Server err) where
240 case Map.lookup a a2bs of
242 Server $ MT.lift $ MT.lift $ MT.throwE
243 ServerErrorPathMissing -- FIXME
246 MT.lift $ MT.lift $ MT.throwE
247 ServerErrorPathMissing -- FIXME
248 go a ((ca, x):xs) = Server $
249 MT.ReaderT $ \env -> MT.StateT $ \st -> do
250 MT.lift (MT.runExceptT (MT.runStateT (MT.runReaderT (unServer x) env) st)) >>= \case
253 MT.runStateT (MT.runReaderT (unServer (choose ra xs)) env) st
255 instance Fileable (Server err) where
256 type FileableConstraint (Server err) = Parsable err
260 Request{..} <- MT.ask
266 List.intercalate "/" $
267 Text.unpack . encodePathSegment <$> requestPathSegments
268 case parse content of
270 Left err -> MT.lift $ MT.lift $ MT.throwE $ ServerErrorParser err
272 -- * Class 'Parsable'
273 class Parsable err a where
274 parse :: BSL.ByteString -> Either err a