1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE KindSignatures #-}
4 {-# LANGUAGE DataKinds #-}
6 {-# LANGUAGE OverloadedStrings #-}
7 {-# LANGUAGE TypeApplications #-}
8 {-# LANGUAGE TypeOperators #-}
9 module Symantic.HTTP.Mime.Type where
11 -- import Text.Show (Show(..))
12 import Data.Function (($))
13 import Data.Functor ((<$>))
14 import Data.Kind (Constraint)
15 import Data.Maybe (Maybe(..))
16 import Data.Proxy (Proxy(..))
17 import Data.Semigroup (Semigroup(..))
18 import Data.Tuple (fst, snd)
19 import Data.Typeable (Typeable)
20 import qualified Data.ByteString as BS
21 import qualified Network.HTTP.Media as Media
23 -- * Class 'MediaTypeable'
24 class MediaTypeable mt where
25 mediaType :: Proxy mt -> MediaType
26 mediaTypes :: Proxy mt -> [MediaType]
27 mediaTypes mt = [mediaType mt]
28 type MediaType = Media.MediaType
29 instance MediaTypeable () where
30 mediaType _mt = mimeAny
32 charsetUTF8 :: MediaType -> MediaType
33 charsetUTF8 = (Media./: ("charset", "utf-8"))
39 data JSON deriving (Typeable)
40 instance MediaTypeable JSON where
41 mediaType _mt = charsetUTF8 $ "application"Media.//"json"
42 mediaTypes mt = [mediaType mt, "application"Media.//"json"]
46 mimeHTML :: Proxy HTML
48 instance MediaTypeable HTML where
49 mediaType _mt = charsetUTF8 $ "text"Media.//"html"
50 mediaTypes mt = [mediaType mt, "text"Media.//"html"]
52 -- ** Type 'FormUrlEncoded'
54 mimeFormUrlEncoded :: Proxy FormUrlEncoded
55 mimeFormUrlEncoded = Proxy
56 instance MediaTypeable FormUrlEncoded where
57 mediaType _mt = "application"Media.//"x-www-form-urlencoded"
59 -- ** Type 'OctetStream'
61 mimeOctetStream :: Proxy OctetStream
62 mimeOctetStream = Proxy
63 instance MediaTypeable OctetStream where
64 mediaType _mt = "application"Media.//"octet-stream"
66 -- ** Type 'PlainText'
68 mimePlainText :: Proxy PlainText
70 instance MediaTypeable PlainText where
71 mediaType _mt = charsetUTF8 $ "text"Media.//"plain"
76 MimeType :: c t => Proxy t -> MimeType c
78 -- ** Type 'MimeTypeT'
79 data MimeTypeT = forall t. MimeTypeT (MimeType t)
81 -- * Class 'MimeTypes'
82 class MimeTypes (ts::[*]) (c:: * -> Constraint) where
83 mimeTypesMap :: [(MediaType, MimeType c)]
86 (MediaTypeable t, c t) =>
87 MimeTypes '[t] c where
89 [ (t, MimeType @c @t Proxy)
90 | t <- mediaTypes (Proxy @t)
99 MimeTypes (t ': t1 ': ts) c where
101 [ (t, MimeType @c @t Proxy)
102 | t <- mediaTypes (Proxy @t)
104 [ (t, MimeType @c @t1 Proxy)
105 | t <- mediaTypes (Proxy @t1)
106 ] <> mimeTypesMap @ts @c
108 listMediaTypes :: forall ts c. MimeTypes ts c => [MediaType]
109 listMediaTypes = fst <$> mimeTypesMap @ts @c
111 listMimeTypes :: forall ts c. MimeTypes ts c => [MimeType c]
112 listMimeTypes = snd <$> mimeTypesMap @ts @c
115 forall ts c. MimeTypes ts c =>
116 BS.ByteString -> Maybe (MimeType c)
117 matchAccept = Media.mapAccept (mimeTypesMap @ts @c)
120 forall ts c. MimeTypes ts c =>
121 BS.ByteString -> Maybe (MimeType c)
122 matchContent = Media.mapContent (mimeTypesMap @ts @c)
125 class MediaTypeable t where
126 mimeTypeParse :: BS.ByteString -> Maybe (MimeType t)
128 -- parseAccept :: Proxy ts -> BS.ByteString -> Maybe (MimeTypeIn ts t)
130 -- mimeDecode :: Proxy mt -> Unserializer a
131 -- * Class 'MimeTypesInj'
133 = MimeTypesInjR ts ts
138 Either Error_MimeType (MimeTypes ts)
139 mimeTypeInj = mimeTypeInjR @_ @ts
141 -- ** Class 'MimeTypesInjR'
142 class MimeTypesInjR (ts::[*]) (rs::[*]) where
143 mimeTypeInjR :: Either Error_MimeType (MimeTypes ts)
144 instance MimeTypesInjR ts '[] where
145 mimeTypeInjR = Right $ MimeTypes mempty
146 instance ( MimeTypeFor ts t
147 , MimeTypesInjR ts rs
148 ) => MimeTypesInjR ts (Proxy t ': rs) where
150 x <- mimeTypeInjR @_ @rs
151 let (n, m) = moduleFor @_ @t
152 MimeTypes (Map.singleton n m) `unionMimeTypes` x
158 -- | Return the position of a type within a list of them.
159 -- This is useful to work around @OverlappingInstances@.
160 type family Index xs x where
161 Index (x ': xs) x = Zero
162 Index (not_x ': xs) x = Succ (Index xs x)
164 -- ** Type 'MimeTypeInj'
165 -- | Convenient type synonym wrapping 'MimeTypePInj'
166 -- applied on the correct 'Index'.
167 type MimeTypeInj ss s = MimeTypeInjP (Index ss (Proxy s)) ss s
169 -- | Inject a given /symantic/ @s@ into a list of them,
170 -- by returning a function which given a 'TeMimeType' on @s@
171 -- returns the same 'TeMimeType' on @ss@.
175 MimeType '[Proxy s] ->
177 mimeTypeInj = mimeTypeInjP @(Index ss (Proxy s))
179 -- *** Class 'MimeTypePInj'
180 class MimeTypeInjP p ss s where
181 mimeTypeInjP :: MimeType '[Proxy s] -> MimeType ss
182 instance MimeTypeInjP Zero (Proxy s ': ss) (s::k) where
183 mimeTypeInjP (MimeType te) = MimeType te
184 instance MimeTypeInjP p ss s => MimeTypeInjP (Succ p) (Proxy not_s ': ss) s where
185 mimeTypeInjP (te::MimeType '[Proxy s]) =
186 case mimeTypeInjP @p te :: MimeType ss of
187 MimeType te' -> MimeType te'