@@ -5,6 +5,9 @@ module Bulletin where
55
66import qualified Web.Twain as Twain
77import 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.
1013main :: IO ()
@@ -13,30 +16,35 @@ main = runServer 3000
1316-- | Run a bulletin-board server at at specific port.
1417runServer :: Port -> IO ()
1518runServer 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