|
| 1 | +{-# LANGUAGE OverloadedStrings #-} |
| 2 | + |
| 3 | +module ShawtyWithReader where |
| 4 | + |
| 5 | +import Control.Monad (replicateM) |
| 6 | +import Control.Monad.IO.Class (liftIO) |
| 7 | +import qualified Data.ByteString.Char8 as BC |
| 8 | +import Data.Text.Encoding (decodeUtf8, encodeUtf8) |
| 9 | +import qualified Data.Text.Lazy as TL |
| 10 | +import qualified Database.Redis as R |
| 11 | +import Network.URI (URI, parseURI) |
| 12 | +import qualified System.Random as SR |
| 13 | +import Web.Scotty |
| 14 | + |
| 15 | + |
| 16 | +newtype Reader r a = |
| 17 | + Reader {runReader :: r -> a} |
| 18 | + |
| 19 | +alphaNum :: String |
| 20 | +alphaNum = ['A'..'Z'] ++ ['0'..'9'] |
| 21 | + |
| 22 | +randomElement :: String -> IO Char |
| 23 | +randomElement xs = do |
| 24 | + let maxIndex :: Int |
| 25 | + maxIndex = length xs - 1 |
| 26 | + -- Right of arrow is IO Int, so randomDigit is Int |
| 27 | + randomDigit <- SR.randomRIO (0, maxIndex) :: IO Int |
| 28 | + return (xs !! randomDigit) |
| 29 | + |
| 30 | +shortyGen :: IO String |
| 31 | +shortyGen = |
| 32 | + replicateM 7 (randomElement alphaNum) |
| 33 | + |
| 34 | +saveURI :: R.Connection |
| 35 | + -> BC.ByteString |
| 36 | + -> BC.ByteString |
| 37 | + -> IO (Either R.Reply R.Status) |
| 38 | +saveURI conn shortURI uri = |
| 39 | + R.runRedis conn $ R.set shortURI uri |
| 40 | + |
| 41 | +getURI :: R.Connection |
| 42 | + -> BC.ByteString |
| 43 | + -> IO (Either R.Reply (Maybe BC.ByteString)) |
| 44 | +getURI conn shortURI = R.runRedis conn $ R.get shortURI |
| 45 | + |
| 46 | +linkShorty :: String -> String |
| 47 | +linkShorty shorty = |
| 48 | + concat [ "<a href=\"" |
| 49 | + , shorty |
| 50 | + , "\">Copy and paste your short URL</a>" |
| 51 | + ] |
| 52 | + |
| 53 | +shortyCreated :: Show a => a -> String -> TL.Text |
| 54 | +shortyCreated resp shawty = |
| 55 | + TL.concat [ TL.pack (show resp) |
| 56 | + , " shorty is: ", TL.pack (linkShorty shawty) |
| 57 | + ] |
| 58 | + |
| 59 | +shortyAintUri :: TL.Text -> TL.Text |
| 60 | +shortyAintUri uri = |
| 61 | + TL.concat [ uri |
| 62 | + , " wasn't a url, did you forget http://?" |
| 63 | + ] |
| 64 | + |
| 65 | +shortyFound :: TL.Text -> TL.Text |
| 66 | +shortyFound tbs = |
| 67 | + TL.concat ["<a href=\"", tbs, "\">", tbs, "</a>"] |
| 68 | + |
| 69 | +data Conn = |
| 70 | + Conn {getConn :: R.Connection} |
| 71 | + |
| 72 | +app :: Conn |
| 73 | + -> ScottyM () |
| 74 | +app = do |
| 75 | + rConn <- getConn |
| 76 | + return $ get "/" $ do |
| 77 | + uri <- param "uri" |
| 78 | + let parsedUri :: Maybe URI |
| 79 | + parsedUri = parseURI $ TL.unpack uri |
| 80 | + case parsedUri of |
| 81 | + Just _ -> do |
| 82 | + shawty <- liftIO shortyGen |
| 83 | + let shorty :: BC.ByteString |
| 84 | + shorty = BC.pack shawty |
| 85 | + uri' :: BC.ByteString |
| 86 | + uri' = encodeUtf8 (TL.toStrict uri) |
| 87 | + resp <- liftIO (saveURI rConn shorty uri') |
| 88 | + html (shortyCreated resp shawty) |
| 89 | + Nothing -> text (shortyAintUri uri) |
| 90 | + return $ get "/:short" $ do |
| 91 | + short <- param "short" |
| 92 | + uri <- liftIO (getURI rConn short) |
| 93 | + case uri of |
| 94 | + Left reply -> text (TL.pack (show reply)) |
| 95 | + Right mbBS -> case mbBS of |
| 96 | + Nothing -> text "uri not found" |
| 97 | + Just bs -> html (shortyFound tbs) |
| 98 | + where tbs :: TL.Text |
| 99 | + tbs = TL.fromStrict (decodeUtf8 bs) |
| 100 | + |
| 101 | +main :: IO () |
| 102 | +main = do |
| 103 | + rConn <- R.connect R.defaultConnectInfo |
| 104 | + scotty 3000 (app (Conn rConn)) |
0 commit comments