2 | import Data.ByteString
3 | import HTTP.API.Encode
4 | import HTTP.API.Decode
6 | import HTTP.Header.Types
7 | import HTTP.RequestErr
10 | import IO.Async.Logging
12 | import Derive.Prelude
13 | import Derive.Finite
16 | %language ElabReflection
19 | data HTTPlang = English | German
21 | %runElab derive "HTTPlang" [Show,Eq,Ord, Finite]
24 | Interpolation HTTPlang where
25 | interpolate English = "English"
26 | interpolate German = "Deutsch"
29 | interface HTTPLocal where
30 | endOfURIPath : String
31 | floatingPointNumber : String
33 | invalidPath : String
35 | logLevel : LogLevel -> String
36 | missingBoundary : String
37 | missingFormDataPart : (part, parts : String) -> String
38 | missingHeader : String -> String
39 | missingQueryParameter : String -> String
40 | missingQueryValue : String -> String
41 | myMediaTypeNotAccepted : String -> String -> String
42 | naturalNumber : String
43 | outOfBounds : Show a => (min,max : a) -> String
44 | prettyDecodeErr : DecodeErr -> String
45 | prettyRequestErr : RequestErr -> String
46 | unsignedInteger : String
54 | valueString : String -> String
56 | valueString s = ": '\{s}'"
58 | dets : DecodeErr -> String
59 | dets (ContentErr _ ds) = ds
60 | dets (ReadErr _ _ ds) = ds
64 | commaSep : List String -> String
65 | commaSep = fastConcat . intersperse ","
68 | commaSepI : Interpolation a => List a -> String
69 | commaSepI = commaSep . map interpolate
72 | commaSepS : Show a => List a -> String
73 | commaSepS = commaSep . map show
75 | parameters {auto loc : HTTPLocal}
78 | Interpolation RequestErr where
79 | interpolate = prettyRequestErr
82 | Interpolation DecodeErr where
83 | interpolate = prettyDecodeErr
86 | decodeErr : Status -> DecodeErr -> RequestErr
87 | decodeErr s de = {message := "\{de}", details := dets de} (requestErr s)
92 | -> {auto r : Decode a}
94 | -> {auto s : Show a}
95 | -> {auto c : Cast a b}
99 | -> Either DecodeErr b
100 | bounded a t min max = refined t (outOfBounds min max) $
\v =>
101 | if (min <= v && v <= max) then Just (cast v) else Nothing
105 | decode (BS 0 _) = Left $
readErr naturalNumber empty
108 | then Right (cast $
decimal bs)
109 | else Left $
readErr naturalNumber bs
112 | Decode Integer where
113 | decode (BS 0 _) = Left $
readErr integer empty
114 | decode bs@(BS (S k) bv) =
115 | mapFst (setType integer) $
case head bv of
116 | 45 => map (negate . cast) (decodeAs Nat (BS k $
tail bv))
117 | _ => map cast $
decodeAs Nat bs
121 | decode = bounded Integer unsignedInteger 0 0xff
124 | Decode Bits16 where
125 | decode = bounded Integer unsignedInteger 0 0xffff
128 | Decode Bits32 where
129 | decode = bounded Integer unsignedInteger 0 0xffff_ffff
132 | Decode Bits64 where
133 | decode = bounded Integer unsignedInteger 0 0xffff_ffff_ffff_ffff
137 | decode = bounded Integer integer (-
0x80) 0x7f
141 | decode = bounded Integer integer (-
0x8000) 0x7fff
145 | decode = bounded Integer integer (-
0x8000_0000) 0x7fff_ffff
149 | decode = bounded Integer integer (-
0x8000_0000_0000_0000) 0x7fff_ffff_ffff_ffff
152 | Decode Double where
154 | case runBytes json bs of
155 | Right (JDouble x) => Right x
156 | Right (JInteger x) => Right $
cast x
157 | _ => Left $
readErr floatingPointNumber bs
159 | parameters {auto fj : FromJSON a}
162 | DecodeVia JSON a where
163 | fromBytes _ = mapFst (contentErr jsonValue) . parseBytes json Virtual
164 | decodeFrom = mapFst (contentErr jsonValue . JErr) . fromJSON
165 | mediaType = applicationJSON
167 | parameters {auto fd : FromFormData a}
170 | DecodeVia FormData a where
172 | case parameter "boundary" ps of
173 | Just b => Right $
multipart (fromString b) bs
174 | Nothing => Left $
Msg missingBoundary
175 | decodeFrom = fromFormData
176 | mediaType = multipartFormData
179 | getFDBytes : String -> FormData -> Either DecodeErr ByteString
181 | case find ((s ==) . name) xs of
182 | Nothing => Left $
Msg $
missingFormDataPart s (commaSep $
map name xs)
183 | Just p => Right p.content