carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

1-- | The shapes the rest of the app talks in.
2module Todo.Model
3 ( ListId (..)
4 , TodoId (..)
5 , Todo (..)
6 , Filter (..)
7 , parseFilter
8 , filterName
9 , filterPath
10 , visible
11 , parseTodoId
12 , todoIdText
13 ) where
14
15import Data.Int (Int64)
16import Data.Text (Text)
17import Data.Text qualified as T
18import Data.Text.Read qualified as T
19
20-- | One visitor's list. Every query about todos is scoped by one.
21newtype ListId = ListId Int64
22 deriving (Eq, Show)
23
24newtype TodoId = TodoId Int64
25 deriving (Eq, Show)
26
27data Todo = Todo
28 { todoId :: !TodoId
29 , todoTitle :: !Text
30 , todoCompleted :: !Bool
31 }
32 deriving (Eq, Show)
33
34data Filter = All | Active | Completed
35 deriving (Eq, Show, Enum, Bounded)
36
37-- | The @filter@ form field. Anything missing or unknown means 'All'.
38parseFilter :: Maybe Text -> Filter
39parseFilter = \case
40 Just "active" -> Active
41 Just "completed" -> Completed
42 _ -> All
43
44filterName :: Filter -> Text
45filterName = \case
46 All -> "all"
47 Active -> "active"
48 Completed -> "completed"
49
50filterPath :: Filter -> Text
51filterPath = \case
52 All -> "/"
53 Active -> "/active"
54 Completed -> "/completed"
55
56visible :: Filter -> Todo -> Bool
57visible = \case
58 All -> const True
59 Active -> not . todoCompleted
60 Completed -> todoCompleted
61
62-- | A todo id as it appears in a path or a query: plain decimal digits,
63-- short enough to fit an 'Int64'.
64parseTodoId :: Text -> Maybe TodoId
65parseTodoId t
66 | T.null t || T.length t > 18 || not (T.all (\c -> c >= '0' && c <= '9') t) = Nothing
67 | otherwise = case T.decimal t of
68 Right (n, rest) | T.null rest -> Just (TodoId n)
69 _ -> Nothing
70
71todoIdText :: TodoId -> Text
72todoIdText (TodoId n) = T.pack (show n)
73