Skip to content

Commit cd077ef

Browse files
committed
Rfc2397 simple tests
1 parent 4ee7076 commit cd077ef

File tree

2 files changed

+58
-27
lines changed

2 files changed

+58
-27
lines changed

pub/functora/src/test/Functora/WebSpec.hs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import qualified Functora.Rfc2397 as Rfc2397
55
import Test.Hspec
66
import Test.QuickCheck.Instances ()
77

8-
dot :: String
9-
dot =
10-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
8+
smaples :: [String]
9+
smaples =
10+
[ "data:text/vnd-example+xyz;foo=bar;base64,R0lGODdh",
11+
"data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678",
12+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
13+
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDADIiJSwlHzIsKSw4NTI7S31RS0VFS5ltc1p9tZ++u7Kfr6zI4f/zyNT/16yv+v/9////////wfD/////////////2wBDATU4OEtCS5NRUZP/zq/O////////////////////////////////////////////////////////////////////wAARCAAYAEADAREAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAQMAAgQF/8QAJRABAAIBBAEEAgMAAAAAAAAAAQIRAAMSITEEEyJBgTORUWFx/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AOgM52xQDrjvAV5Xv0vfKUALlTQfeBm0HThMNHXkL0Lw/swN5qgA8yT4MCS1OEOJV8mBz9Z05yfW8iSx7p4j+jA1aD6Wj7ZMzstsfvAas4UyRHvjrAkC9KhpLMClQntlqFc2X1gUj4viwVObKrddH9YDoHvuujAEuNV+bLwFS8XxdSr+Cq3Vf+4F5RgQl6ZR2p1eAzU/HX80YBYyJLCuexwJCO2O1bwCRidAfWBSctswbI12GAJT3yiwFR7+MBjGK2g/WAJR3FdF84E2rK5VR0YH/9k="
14+
]
1115

1216
spec :: Spec
1317
spec = do
14-
it "encode/decode" $
15-
fmap (uncurry Rfc2397.encode) (Rfc2397.decode dot)
16-
`shouldBe` Just dot
18+
it "encode/decode" . forM_ smaples $ \sample ->
19+
fmap Rfc2397.encode (Rfc2397.decode sample) `shouldBe` Just sample

pub/functora/src/web/Functora/Rfc2397.hs

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,68 @@
11
module Functora.Rfc2397
2-
( encode,
2+
( Media (..),
3+
Encoding (..),
4+
encode,
35
decode,
46
)
57
where
68

79
import qualified Data.ByteString.Base64 as B64
810
import Functora.Prelude
11+
import qualified Network.URI as URI
912
import qualified Network.URI.Encode as UE
1013
import qualified Prelude
1114

12-
data ENC = BASE64 | URL
13-
deriving stock (Eq)
15+
data Media = Media
16+
{ mediaMime :: String,
17+
mediaBytes :: ByteString,
18+
mediaEncoding :: Encoding
19+
}
20+
deriving stock (Eq, Ord, Show, Data, Generic)
1421

15-
encode :: String -> ByteString -> String
16-
encode mime bs =
22+
data Encoding = B64Encoding | UrlEncoding
23+
deriving stock (Eq, Ord, Show, Read, Data, Generic, Bounded, Enum)
24+
25+
encode :: Media -> String
26+
encode Media {mediaMime = mime, mediaBytes = bs, mediaEncoding = enc} =
1727
"data:"
1828
<> mime
19-
<> ";base64,"
20-
<> either impureThrow id (decodeUtf8Strict $ B64.encode bs)
29+
<> ( case enc of
30+
B64Encoding -> ";base64"
31+
UrlEncoding -> mempty
32+
)
33+
<> ","
34+
<> ( case enc of
35+
B64Encoding ->
36+
either impureThrow id
37+
. decodeUtf8Strict
38+
$ B64.encode bs
39+
UrlEncoding ->
40+
either impureThrow (UE.encodeWith URI.isAllowedInURI)
41+
$ decodeUtf8Strict bs
42+
)
2143

22-
decode :: String -> Maybe (String, ByteString)
44+
decode :: String -> Maybe Media
2345
decode url =
2446
let (scheme, rest) = break (== ':') url
2547
in case rest of
2648
':' : contents | scheme == "data" -> decodeContents contents
2749
_ -> Nothing
2850

29-
decodeContents :: String -> Maybe (String, ByteString)
51+
decodeContents :: String -> Maybe Media
3052
decodeContents xs =
3153
let (prefix, restdata) = break (== ',') xs
3254
in case restdata of
3355
',' : thedata -> decodePrefix prefix thedata
3456
_ -> Nothing
3557

36-
decodePrefix :: String -> String -> Maybe (String, ByteString)
58+
decodePrefix :: String -> String -> Maybe Media
3759
decodePrefix prefix thedata =
3860
let fragments = breakList (== ';') prefix
3961
enc = case reverse fragments of
40-
("base64" : _) -> BASE64
41-
_ -> URL
62+
("base64" : _) -> B64Encoding
63+
_ -> UrlEncoding
4264
mediapart
43-
| enc == BASE64 = Prelude.init fragments
65+
| enc == B64Encoding = Prelude.init fragments
4466
| otherwise = fragments
4567
in case mediapart of
4668
(xs : _) ->
@@ -49,14 +71,20 @@ decodePrefix prefix thedata =
4971
_ -> decodeData mediapart enc thedata
5072
_ -> decodeData ["text/plain", "charset=US-ASCII"] enc thedata
5173

52-
decodeData :: [String] -> ENC -> String -> Maybe (String, ByteString)
53-
decodeData mediatype enc thedata =
54-
Just
55-
( unparse mediatype,
56-
case enc of
57-
URL -> encodeUtf8 $ UE.decode thedata
58-
BASE64 -> either Prelude.error id . B64.decode $ encodeUtf8 thedata
59-
)
74+
decodeData :: [String] -> Encoding -> String -> Maybe Media
75+
decodeData mediatype enc thedata = do
76+
bs <-
77+
case enc of
78+
UrlEncoding ->
79+
pure . encodeUtf8 $ UE.decode thedata
80+
B64Encoding ->
81+
either (const Nothing) Just . B64.decode $ encodeUtf8 thedata
82+
pure
83+
Media
84+
{ mediaMime = unparse mediatype,
85+
mediaBytes = bs,
86+
mediaEncoding = enc
87+
}
6088

6189
breakList :: (x -> Bool) -> [x] -> [[x]]
6290
breakList p xs =

0 commit comments

Comments
 (0)