2 | import Data.ByteString
3 | import HTTP.API.Decode
5 | import HTTP.Header.Types
6 | import HTTP.RequestErr
9 | import IO.Async.Logging
11 | import Derive.Prelude
12 | import Derive.Finite
15 | %language ElabReflection
18 | data HTTPlang = English | German
20 | %runElab derive "HTTPlang" [Show,Eq,Ord, Finite]
23 | Interpolation HTTPlang where
24 | interpolate English = "English"
25 | interpolate German = "Deutsch"
28 | interface HTTPLocal where
29 | endOfURIPath : String
30 | floatingPointNumber : String
32 | invalidPath : String
34 | logLevel : LogLevel -> String
35 | missingBoundary : String
36 | missingFormDataPart : (part, parts : String) -> String
37 | missingHeader : String -> String
38 | missingQueryParameter : String -> String
39 | missingQueryValue : String -> String
40 | myMediaTypeNotAccepted : String -> String -> String
41 | naturalNumber : String
42 | outOfBounds : Show a => (min,max : a) -> String
43 | prettyDecodeErr : DecodeErr -> String
44 | prettyRequestErr : RequestErr -> String
45 | unsignedInteger : String
53 | valueString : String -> String
55 | valueString s = ": '\{s}'"
57 | dets : DecodeErr -> String
58 | dets (ContentErr _ ds) = ds
59 | dets (ReadErr _ _ ds) = ds
63 | commaSep : List String -> String
64 | commaSep = fastConcat . intersperse ","
67 | commaSepI : Interpolation a => List a -> String
68 | commaSepI = commaSep . map interpolate
71 | commaSepS : Show a => List a -> String
72 | commaSepS = commaSep . map show
74 | parameters {auto loc : HTTPLocal}
77 | Interpolation RequestErr where
78 | interpolate = prettyRequestErr
81 | Interpolation DecodeErr where
82 | interpolate = prettyDecodeErr
85 | decodeErr : Status -> DecodeErr -> RequestErr
86 | decodeErr s de = {message := "\{de}", details := dets de} (requestErr s)
91 | -> {auto r : Decode a}
93 | -> {auto s : Show a}
94 | -> {auto c : Cast a b}
98 | -> Either DecodeErr b
99 | bounded a t min max = refined t (outOfBounds min max) $
\v =>
100 | if (min <= v && v <= max) then Just (cast v) else Nothing
104 | decode (BS 0 _) = Left $
readErr naturalNumber empty
107 | then Right (cast $
decimal bs)
108 | else Left $
readErr naturalNumber bs
111 | Decode Integer where
112 | decode (BS 0 _) = Left $
readErr integer empty
113 | decode bs@(BS (S k) bv) =
114 | mapFst (setType integer) $
case head bv of
115 | 45 => map (negate . cast) (decodeAs Nat (BS k $
tail bv))
116 | _ => map cast $
decodeAs Nat bs
120 | decode = bounded Integer unsignedInteger 0 0xff
123 | Decode Bits16 where
124 | decode = bounded Integer unsignedInteger 0 0xffff
127 | Decode Bits32 where
128 | decode = bounded Integer unsignedInteger 0 0xffff_ffff
131 | Decode Bits64 where
132 | decode = bounded Integer unsignedInteger 0 0xffff_ffff_ffff_ffff
136 | decode = bounded Integer integer (-
0x80) 0x7f
140 | decode = bounded Integer integer (-
0x8000) 0x7fff
144 | decode = bounded Integer integer (-
0x8000_0000) 0x7fff_ffff
148 | decode = bounded Integer integer (-
0x8000_0000_0000_0000) 0x7fff_ffff_ffff_ffff
151 | Decode Double where
153 | case runBytes json bs of
154 | Right (JDouble x) => Right x
155 | Right (JInteger x) => Right $
cast x
156 | _ => Left $
readErr floatingPointNumber bs
158 | parameters {auto fj : FromJSON a}
161 | DecodeVia JSON a where
162 | fromBytes _ = mapFst (contentErr jsonValue) . parseBytes json Virtual
163 | decodeFrom = mapFst (contentErr jsonValue . JErr) . fromJSON
164 | mediaType = MT "application" "json"
166 | parameters {auto fd : FromFormData a}
169 | DecodeVia FormData a where
171 | case parameter "boundary" ps of
172 | Just b => Right $
multipart (fromString b) bs
173 | Nothing => Left $
Msg missingBoundary
174 | decodeFrom = fromFormData
175 | mediaType = MT "multipart" "form-data"
178 | getFDBytes : String -> FormData -> Either DecodeErr ByteString
180 | case find ((s ==) . name) xs of
181 | Nothing => Left $
Msg $
missingFormDataPart s (commaSep $
map name xs)
182 | Just p => Right p.content