|
| 1 | +module Functora.Rfc2397 |
| 2 | + ( encode, |
| 3 | + decode, |
| 4 | + ) |
| 5 | +where |
| 6 | + |
| 7 | +import qualified Data.ByteString.Base64 as B64 |
| 8 | +import Functora.Prelude |
| 9 | +import qualified Network.URI.Encode as UE |
| 10 | +import qualified Prelude |
| 11 | + |
| 12 | +data ENC = BASE64 | URL |
| 13 | + deriving stock (Eq) |
| 14 | + |
| 15 | +encode :: String -> ByteString -> String |
| 16 | +encode mime bs = |
| 17 | + "data:" |
| 18 | + <> mime |
| 19 | + <> ";base64," |
| 20 | + <> either impureThrow id (decodeUtf8Strict $ B64.encode bs) |
| 21 | + |
| 22 | +decode :: String -> Maybe (String, ByteString) |
| 23 | +decode url = |
| 24 | + let (scheme, rest) = break (== ':') url |
| 25 | + in case rest of |
| 26 | + ':' : contents | scheme == "data" -> decodeContents contents |
| 27 | + _ -> Nothing |
| 28 | + |
| 29 | +decodeContents :: String -> Maybe (String, ByteString) |
| 30 | +decodeContents xs = |
| 31 | + let (prefix, restdata) = break (== ',') xs |
| 32 | + in case restdata of |
| 33 | + ',' : thedata -> decodePrefix prefix thedata |
| 34 | + _ -> Nothing |
| 35 | + |
| 36 | +decodePrefix :: String -> String -> Maybe (String, ByteString) |
| 37 | +decodePrefix prefix thedata = |
| 38 | + let fragments = breakList (== ';') prefix |
| 39 | + enc = case reverse fragments of |
| 40 | + ("base64" : _) -> BASE64 |
| 41 | + _ -> URL |
| 42 | + mediapart |
| 43 | + | enc == BASE64 = Prelude.init fragments |
| 44 | + | otherwise = fragments |
| 45 | + in case mediapart of |
| 46 | + (xs : _) -> |
| 47 | + case break (== '/') xs of |
| 48 | + (_, []) -> decodeData ("text/plain" : mediapart) enc thedata |
| 49 | + _ -> decodeData mediapart enc thedata |
| 50 | + _ -> decodeData ["text/plain", "charset=US-ASCII"] enc thedata |
| 51 | + |
| 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 | + ) |
| 60 | + |
| 61 | +breakList :: (x -> Bool) -> [x] -> [[x]] |
| 62 | +breakList p xs = |
| 63 | + let (pre, post) = break p xs |
| 64 | + in case post of |
| 65 | + [] -> [pre] |
| 66 | + _ : ys -> pre : breakList p ys |
| 67 | + |
| 68 | +unparse :: [String] -> String |
| 69 | +unparse [] = "" |
| 70 | +unparse [xs] = xs |
| 71 | +unparse (xs : xss) = xs ++ ';' : unparse xss |
0 commit comments