{-|

Module      : WeekDay
Description : Enumerate week days
Copyright   : © Frank Jung, 2022
License     : GPL-3

A simple demonstration on how to map to and from a bounded, enumerated data
type.

In this example we use the weekdays names as our type to iterate across.

  * 'makeWeekday' is a simple conversion function to map a 'Weekday'
     from a string
  * 'fullWeek' returns a list of all abbreviated weekdays
  * 'capitalise' returns the title case of a string

The 'makeWeekday' code is rather weak as any word where the first 3 letters
map to a weekday is valid for conversion.

== Other Examples

=== Using enum:

This example shows how use 'Weekday' from an enumeration:

>>> toEnum 0 :: WeekDay
Mon

-}

module Weekday ( Weekday(..)
               , makeWeekday
               , fullWeek
               , capitalise
               ) where

import           Data.Char       (toLower, toTitle)
import           Test.QuickCheck (Arbitrary (arbitrary), arbitraryBoundedEnum)
import           Text.Read       (readMaybe)

-- | Abbreviated days of the week from Mon (Monday) to Sun (Sunday).
data Weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun
                deriving (Weekday -> Weekday -> Bool
(Weekday -> Weekday -> Bool)
-> (Weekday -> Weekday -> Bool) -> Eq Weekday
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Weekday -> Weekday -> Bool
== :: Weekday -> Weekday -> Bool
$c/= :: Weekday -> Weekday -> Bool
/= :: Weekday -> Weekday -> Bool
Eq, Int -> Weekday
Weekday -> Int
Weekday -> [Weekday]
Weekday -> Weekday
Weekday -> Weekday -> [Weekday]
Weekday -> Weekday -> Weekday -> [Weekday]
(Weekday -> Weekday)
-> (Weekday -> Weekday)
-> (Int -> Weekday)
-> (Weekday -> Int)
-> (Weekday -> [Weekday])
-> (Weekday -> Weekday -> [Weekday])
-> (Weekday -> Weekday -> [Weekday])
-> (Weekday -> Weekday -> Weekday -> [Weekday])
-> Enum Weekday
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: Weekday -> Weekday
succ :: Weekday -> Weekday
$cpred :: Weekday -> Weekday
pred :: Weekday -> Weekday
$ctoEnum :: Int -> Weekday
toEnum :: Int -> Weekday
$cfromEnum :: Weekday -> Int
fromEnum :: Weekday -> Int
$cenumFrom :: Weekday -> [Weekday]
enumFrom :: Weekday -> [Weekday]
$cenumFromThen :: Weekday -> Weekday -> [Weekday]
enumFromThen :: Weekday -> Weekday -> [Weekday]
$cenumFromTo :: Weekday -> Weekday -> [Weekday]
enumFromTo :: Weekday -> Weekday -> [Weekday]
$cenumFromThenTo :: Weekday -> Weekday -> Weekday -> [Weekday]
enumFromThenTo :: Weekday -> Weekday -> Weekday -> [Weekday]
Enum, Int -> Weekday -> ShowS
[Weekday] -> ShowS
Weekday -> String
(Int -> Weekday -> ShowS)
-> (Weekday -> String) -> ([Weekday] -> ShowS) -> Show Weekday
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Weekday -> ShowS
showsPrec :: Int -> Weekday -> ShowS
$cshow :: Weekday -> String
show :: Weekday -> String
$cshowList :: [Weekday] -> ShowS
showList :: [Weekday] -> ShowS
Show, Weekday
Weekday -> Weekday -> Bounded Weekday
forall a. a -> a -> Bounded a
$cminBound :: Weekday
minBound :: Weekday
$cmaxBound :: Weekday
maxBound :: Weekday
Bounded, ReadPrec [Weekday]
ReadPrec Weekday
Int -> ReadS Weekday
ReadS [Weekday]
(Int -> ReadS Weekday)
-> ReadS [Weekday]
-> ReadPrec Weekday
-> ReadPrec [Weekday]
-> Read Weekday
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS Weekday
readsPrec :: Int -> ReadS Weekday
$creadList :: ReadS [Weekday]
readList :: ReadS [Weekday]
$creadPrec :: ReadPrec Weekday
readPrec :: ReadPrec Weekday
$creadListPrec :: ReadPrec [Weekday]
readListPrec :: ReadPrec [Weekday]
Read)

-- | Provide a random instance of a 'Weekday'.
--
-- This is for ease of use in property testing.
--
-- >>> generate arbitrary :: IO Weekday
-- Wed
instance Arbitrary Weekday where
  arbitrary :: Gen Weekday
arbitrary = Gen Weekday
forall a. (Bounded a, Enum a) => Gen a
arbitraryBoundedEnum

-- | Read a 'Weekday' from a String.
--
-- Some string to 'Weekday' examples:
--
-- >>> makeWeekday "tHU"
-- Just Thu
--
-- >>> makeWeekday "sun"
-- Just Sun
--
-- >>> makeWeekday "Bad"
-- Nothing
--
-- Any string bigger than 3 characters also fails:
-- >>> makeWeekday "Mond"
-- Nothing
makeWeekday :: String -> Maybe Weekday
makeWeekday :: String -> Maybe Weekday
makeWeekday = String -> Maybe Weekday
forall a. Read a => String -> Maybe a
readMaybe (String -> Maybe Weekday) -> ShowS -> String -> Maybe Weekday
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
capitalise ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> ShowS
forall a. Int -> [a] -> [a]
take Int
4

-- | List all days of the week.
--
-- >>> fullWeek
-- [Mon,Tue,Wed,Thu,Fri,Sat,Sun]
fullWeek :: [Weekday]
fullWeek :: [Weekday]
fullWeek = [Weekday
forall a. Bounded a => a
minBound..Weekday
forall a. Bounded a => a
maxBound]

-- | Return title case of string.
--
-- >>> capitalise "monday"
-- "Monday"
--
-- >>> null (capitalise "")
-- True
capitalise :: String -> String
capitalise :: ShowS
capitalise = ((Char -> Char) -> Char -> Char) -> [Char -> Char] -> ShowS
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (Char -> Char) -> Char -> Char
forall a. a -> a
id (Char -> Char
toTitle (Char -> Char) -> [Char -> Char] -> [Char -> Char]
forall a. a -> [a] -> [a]
: (Char -> Char) -> [Char -> Char]
forall a. a -> [a]
repeat Char -> Char
toLower)