Skip to content

Commit e5d3ddf

Browse files
committed
display posts
1 parent d8f9f86 commit e5d3ddf

1 file changed

Lines changed: 89 additions & 10 deletions

File tree

src/Bulletin.hs

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ module Bulletin where
55

66
import qualified Web.Twain as Twain
77
import Network.Wai.Handler.Warp (run, Port)
8+
import qualified Data.Text as T
9+
import qualified Data.Time.Clock as C
10+
import qualified Data.Map as M
811

912
-- | Entry point. Starts a bulletin-board server at port 3000.
1013
main :: IO ()
@@ -13,30 +16,35 @@ main = runServer 3000
1316
-- | Run a bulletin-board server at at specific port.
1417
runServer :: Port -> IO ()
1518
runServer port = do
19+
app <- mkApp
1620
putStrLn $ unwords
1721
[ "Running bulletin board app at"
1822
, "http://localhost:" <> show port
1923
, "(ctrl-c to quit)"
2024
]
21-
run port mkApp
25+
run port app
26+
27+
-- ** Application and routing
2228

2329
-- | Bulletin board application description.
24-
mkApp :: Twain.Application
25-
mkApp =
26-
foldr ($)
30+
mkApp :: IO Twain.Application
31+
mkApp = do
32+
dummyPosts <- makeDummyPosts
33+
pure $ foldr ($)
2734
(Twain.notFound $ Twain.send $ Twain.text "Error: not found.")
28-
routes
35+
(routes dummyPosts)
2936

3037
-- | Bulletin board routing.
31-
routes :: [Twain.Middleware]
32-
routes =
38+
routes :: Posts -> [Twain.Middleware]
39+
routes posts =
3340
-- Our main page, which will display all of the bulletins
3441
[ Twain.get "/" $
35-
Twain.send $ Twain.text "not yet implemented"
42+
Twain.send (displayAllPosts posts)
3643

3744
-- A page for a specific post
38-
, Twain.get "/post/:id" $
39-
Twain.send $ Twain.text "not yet implemented"
45+
, Twain.get "/post/:id" $ do
46+
pid <- Twain.param "id"
47+
Twain.send (displayPost pid posts)
4048

4149
-- A page for creating a new post
4250
, Twain.get "/new" $
@@ -50,3 +58,74 @@ routes =
5058
, Twain.post "/post/:id/delete" $
5159
Twain.send $ Twain.text "not yet implemented"
5260
]
61+
62+
-- ** Business logic
63+
64+
-- | Respond with a list of all posts
65+
displayAllPosts :: Posts -> Twain.Response
66+
displayAllPosts =
67+
Twain.text . T.unlines . map ppPost . M.elems
68+
69+
-- | Respond with a specific post or return 404
70+
displayPost :: Integer -> Posts -> Twain.Response
71+
displayPost pid posts =
72+
case M.lookup pid posts of
73+
Just post ->
74+
Twain.text (ppPost post)
75+
76+
Nothing ->
77+
Twain.raw
78+
Twain.status404
79+
[("Content-Type", "text/plain; charset=utf-8")]
80+
"404 Not found."
81+
82+
-- ** Posts
83+
84+
-- | A mapping from a post id to a post.
85+
type Posts = M.Map Integer Post
86+
87+
-- | A description of a bulletin board post.
88+
data Post
89+
= Post
90+
{ pTime :: C.UTCTime
91+
, pAuthor :: T.Text
92+
, pTitle :: T.Text
93+
, pContent :: T.Text
94+
}
95+
96+
-- | Create an initial posts Map with a dummy post.
97+
makeDummyPosts :: IO Posts
98+
makeDummyPosts = do
99+
time <- C.getCurrentTime
100+
pure $
101+
M.singleton
102+
0
103+
( Post
104+
{ pTime = time
105+
, pTitle = "Dummy title"
106+
, pAuthor = "Dummy author"
107+
, pContent = "bla bla bla..."
108+
}
109+
)
110+
111+
-- | Prettyprint a post to text.
112+
ppPost :: Post -> T.Text
113+
ppPost post =
114+
let
115+
header =
116+
T.unwords
117+
[ "[" <> T.pack (show (pTime post)) <> "]"
118+
, pTitle post
119+
, "by"
120+
, pAuthor post
121+
]
122+
seperator =
123+
T.replicate (T.length header) "-"
124+
in
125+
T.unlines
126+
[ seperator
127+
, header
128+
, seperator
129+
, pContent post
130+
, seperator
131+
]

0 commit comments

Comments
 (0)