Skip to content

Commit 89d31dd

Browse files
authored
Merge pull request #20 from alan-turing-institute/version
Add --version and --color options
2 parents b43d59e + a2edf04 commit 89d31dd

File tree

11 files changed

+139
-94
lines changed

11 files changed

+139
-94
lines changed

app/Args.hs

+15-24
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
module Args (Args (..), getArgs) where
22

3+
import Data.List.NonEmpty (NonEmpty)
34
import qualified Data.Text as T
45
import Data.Time.Calendar (Day)
5-
import Entities (Days (..), Minutes (..), Person (..))
6+
import Data.Version (showVersion)
7+
import Data.Word (Word8)
8+
import Meet.Args (colorFlag, localSwitch, startDateFlag)
9+
import Meet.Entities (Days (..), Minutes (..), Person (..))
610
import Options.Applicative
7-
import Text.Read (readMaybe)
11+
import Paths_meet (version)
812

913
data Args = Args
1014
{ argsEmails :: [Person],
@@ -14,7 +18,8 @@ data Args = Args
1418
argsTimespan :: Days,
1519
argsInPerson :: Int,
1620
argsFeelingLucky :: Bool,
17-
argsShowLocalTime :: Bool
21+
argsShowLocalTime :: Bool,
22+
argsColors :: Maybe (NonEmpty Word8)
1823
}
1924
deriving (Eq, Show)
2025

@@ -38,15 +43,7 @@ parseArgs =
3843
<> help "Duration of the meeting. Defaults to 60 minutes."
3944
<> value (Minutes 60)
4045
)
41-
<*> optional
42-
( option
43-
readDate
44-
( long "startDate"
45-
<> short 's'
46-
<> metavar "YYYY-MM-DD"
47-
<> help "First day to start searching for a meeting on."
48-
)
49-
)
46+
<*> startDateFlag
5047
<*> option
5148
(Days <$> auto)
5249
( long "timespan"
@@ -68,17 +65,8 @@ parseArgs =
6865
<> short 'l'
6966
<> help "Make the app suggest a single best meeting time (and room if needed)."
7067
)
71-
<*> switch
72-
( long "local"
73-
<> help "Display meeting times in your local timezone. By default, times are shown in London time."
74-
)
75-
76-
readDate :: ReadM Day
77-
readDate = do
78-
s <- str
79-
case readMaybe s of
80-
Just d -> pure d
81-
Nothing -> error "Date must be specified in YYYY-MM-DD format"
68+
<*> localSwitch
69+
<*> colorFlag
8270

8371
readPerson :: ReadM Person
8472
readPerson = do
@@ -90,7 +78,10 @@ readPerson = do
9078
else val <> "@turing.ac.uk"
9179

9280
opts :: ParserInfo Args
93-
opts = info (parseArgs <**> helper) (fullDesc <> progDesc "Schedule a meeting with the given emails." <> header "meet - a tool to schedule a meeting")
81+
opts =
82+
info
83+
(parseArgs <**> helper <**> simpleVersioner ("meet version " ++ showVersion version))
84+
(fullDesc <> progDesc "Schedule a meeting with the given emails." <> header ("meet - a tool to schedule a meeting"))
9485

9586
getArgs :: IO Args
9687
getArgs = execParser opts

app/Main.hs

+7-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
module Main where
44

55
import Args (Args (..), getArgs)
6-
import Azure (fetchSchedules, getToken)
76
import Control.Monad (when)
87
import qualified Data.List.NonEmpty as NE
98
import qualified Data.Text as T
@@ -16,11 +15,12 @@ import Data.Time.LocalTime
1615
localTimeToUTC,
1716
timeZoneOffsetString,
1817
)
19-
import Entities (Days (..), Room (..), allRooms)
20-
import Meetings (chooseBestMeeting, getMeetings)
21-
import Print (infoPrint, prettyPrint)
18+
import Meet.Azure (fetchSchedules, getToken)
19+
import Meet.Entities (Days (..), Room (..), allRooms)
20+
import Meet.Meetings (chooseBestMeeting, getMeetings)
21+
import Meet.Print (infoPrint, prettyPrint)
22+
import Meet.Utils
2223
import System.Exit (exitSuccess)
23-
import Utils
2424

2525
main :: IO ()
2626
main = do
@@ -31,7 +31,6 @@ main = do
3131
searchStartDate = argsStartDate args
3232
searchSpanDays = argsTimespan args
3333
inPerson = argsInPerson args
34-
showInLocalTime = argsShowLocalTime args
3534
nChunks <- gracefulDivide durationMinutes intervalMinutes
3635

3736
-- Default start date is today but in London
@@ -56,13 +55,13 @@ main = do
5655
let goodMeetings = getMeetings personSchs roomSchs inPerson nChunks startTime' intervalMinutes londonTz
5756

5857
-- Display times in London unless otherwise specified
59-
displayTz <- if showInLocalTime then getCurrentTimeZone else pure londonTz
58+
displayTz <- if argsShowLocalTime args then getCurrentTimeZone else pure londonTz
6059
let displayTzText = T.pack $ "UTC" <> timeZoneOffsetString displayTz
6160

6261
case NE.nonEmpty goodMeetings of
6362
Nothing -> T.putStrLn "No meetings were available. :("
6463
Just ms -> do
6564
if argsFeelingLucky args
6665
then infoPrint displayTz (chooseBestMeeting ms) inPerson
67-
else prettyPrint displayTz goodMeetings
66+
else prettyPrint (argsColors args) displayTz goodMeetings
6867
T.putStrLn $ "All times are in " <> displayTzText <> "."

meet.cabal

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 3.0
22
name: meet
3-
version: 0.2.0.3
3+
version: 0.2.0.4
44
license: MIT
55
license-file: LICENSE
66
maintainer: [email protected]
@@ -17,11 +17,12 @@ extra-doc-files: CHANGELOG.md
1717

1818
library
1919
exposed-modules:
20-
Azure
21-
Entities
22-
Meetings
23-
Print
24-
Utils
20+
Meet.Args
21+
Meet.Azure
22+
Meet.Entities
23+
Meet.Meetings
24+
Meet.Print
25+
Meet.Utils
2526

2627
hs-source-dirs: src
2728
default-language: Haskell2010
@@ -45,11 +46,12 @@ library
4546
unix >= 2.8.4 && < 2.9,
4647
bytestring >= 0.12.1.0 && < 0.13,
4748
binary >= 0.8.9.1 && < 0.9,
49+
optparse-applicative >= 0.18.1 && < 0.19,
4850

4951
executable meet
5052
main-is: Main.hs
5153
hs-source-dirs: app
52-
other-modules: Args
54+
other-modules: Args, Paths_meet
5355
default-language: Haskell2010
5456
default-extensions: OverloadedStrings ScopedTypeVariables
5557
ghc-options: -Wall
@@ -63,7 +65,7 @@ executable meet
6365
executable meet-rooms
6466
main-is: Main.hs
6567
hs-source-dirs: rooms
66-
other-modules: Args
68+
other-modules: Args, Paths_meet
6769
default-language: Haskell2010
6870
default-extensions: OverloadedStrings ScopedTypeVariables
6971
ghc-options: -Wall

rooms/Args.hs

+15-24
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
module Args (Args (..), getArgs) where
22

3+
import Data.List.NonEmpty (NonEmpty)
34
import Data.Time.Calendar (Day)
4-
import Entities (Days (..))
5+
import Data.Version (showVersion)
6+
import Data.Word (Word8)
7+
import Meet.Args (colorFlag, localSwitch, startDateFlag)
8+
import Meet.Entities (Days (..))
59
import Options.Applicative
6-
import Text.Read (readMaybe)
10+
import Paths_meet (version)
711

812
data Args = Args
913
{ argsStartDate :: Maybe Day,
1014
argsTimespan :: Days,
1115
argsCapacity :: Int,
12-
argsShowLocalTime :: Bool
16+
argsShowLocalTime :: Bool,
17+
argsColors :: Maybe (NonEmpty Word8)
1318
}
1419
deriving (Eq, Show)
1520

1621
parseArgs :: Parser Args
1722
parseArgs =
1823
Args
19-
<$> optional
20-
( option
21-
readDate
22-
( long "startDate"
23-
<> short 's'
24-
<> metavar "YYYY-MM-DD"
25-
<> help "First day to start searching for a meeting room on."
26-
)
27-
)
24+
<$> startDateFlag
2825
<*> option
2926
(Days <$> auto)
3027
( long "timespan"
@@ -41,20 +38,14 @@ parseArgs =
4138
<> help "Minimum capacity needed for the meeting room. Defaults to 0."
4239
<> value 0
4340
)
44-
<*> switch
45-
( long "local"
46-
<> help "Display meeting times in your local timezone. By default, times are shown in London time."
47-
)
48-
49-
readDate :: ReadM Day
50-
readDate = do
51-
s <- str
52-
case readMaybe s of
53-
Just d -> pure d
54-
Nothing -> error "Date must be specified in YYYY-MM-DD format"
41+
<*> localSwitch
42+
<*> colorFlag
5543

5644
opts :: ParserInfo Args
57-
opts = info (parseArgs <**> helper) (fullDesc <> progDesc "Find a meeting room" <> header "meet-rooms - find a meeting room for a pre-existing meeting")
45+
opts =
46+
info
47+
(parseArgs <**> helper <**> simpleVersioner ("meet-rooms version " ++ showVersion version))
48+
(fullDesc <> progDesc "Find a meeting room" <> header ("meet-rooms - find a meeting room for a pre-existing meeting"))
5849

5950
getArgs :: IO Args
6051
getArgs = execParser opts

rooms/Main.hs

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
module Main where
44

55
import Args (Args (..), getArgs)
6-
import Azure (fetchSchedules, getToken)
76
import Control.Monad (when)
87
import qualified Data.Text as T
98
import qualified Data.Text.IO as T
109
import Data.Time.Calendar (addDays)
1110
import Data.Time.LocalTime (LocalTime (..), TimeOfDay (..), getCurrentTimeZone, localTimeToUTC, timeZoneOffsetString)
12-
import Entities (Days (..), Minutes (..), Room (..), allRooms, schedule)
13-
import Meetings (getRoomMeetings)
14-
import Print (prettyPrint)
11+
import Meet.Azure (fetchSchedules, getToken)
12+
import Meet.Entities (Days (..), Minutes (..), Room (..), allRooms, schedule)
13+
import Meet.Meetings (getRoomMeetings)
14+
import Meet.Print (prettyPrint)
15+
import Meet.Utils
1516
import System.Exit (exitSuccess)
16-
import Utils
1717

1818
main :: IO ()
1919
main = do
@@ -52,7 +52,7 @@ main = do
5252
let displayTzText = T.pack $ "UTC" <> timeZoneOffsetString displayTz
5353

5454
case goodMeetings of
55-
[] -> T.putStrLn "No meetings were available. :("
55+
[] -> T.putStrLn "No rooms were available. :("
5656
_ -> do
57-
prettyPrint displayTz goodMeetings
57+
prettyPrint (argsColors args) displayTz goodMeetings
5858
T.putStrLn $ "All times are in " <> displayTzText <> "."

src/Meet/Args.hs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
-- | Shared argument parsing behaviour
2+
module Meet.Args (localSwitch, colorFlag, startDateFlag) where
3+
4+
import Data.List.NonEmpty (NonEmpty)
5+
import qualified Data.List.NonEmpty as NE
6+
import Data.Text (Text)
7+
import qualified Data.Text as T
8+
import Data.Time.Calendar (Day)
9+
import Data.Word (Word8)
10+
import Options.Applicative
11+
import Text.Read (readMaybe)
12+
13+
localSwitch :: Parser Bool
14+
localSwitch =
15+
switch
16+
( long "local"
17+
<> help "Display meeting times in your local timezone. By default, times are shown in London time."
18+
)
19+
20+
colorFlag :: Parser (Maybe (NonEmpty Word8))
21+
colorFlag =
22+
option
23+
readColors
24+
( long "color"
25+
<> short 'c'
26+
<> metavar "COLOR"
27+
<> help "Colours to be used for output table formatting. Colours here refer to 256-colour terminal colours, and are specified as a comma-separated list of integers between 0 and 255 (inclusive). Pass a value of 'none' to remove colours. Defaults to '35,128', which is green and purple."
28+
<> value (Just $ NE.fromList [35, 128])
29+
)
30+
31+
startDateFlag :: Parser (Maybe Day)
32+
startDateFlag =
33+
optional
34+
( option
35+
readDate
36+
( long "startDate"
37+
<> short 's'
38+
<> metavar "YYYY-MM-DD"
39+
<> help "First day to start searching for a meeting on."
40+
)
41+
)
42+
43+
readDate :: ReadM Day
44+
readDate = do
45+
s <- str
46+
case readMaybe s of
47+
Just d -> pure d
48+
Nothing -> error "Date must be specified in YYYY-MM-DD format"
49+
50+
readColors :: ReadM (Maybe (NonEmpty Word8))
51+
readColors = do
52+
val <- str
53+
pure $ case val of
54+
"none" -> Nothing
55+
_ -> Just $ NE.fromList $ map (read . T.unpack) $ T.splitOn "," val

src/Azure.hs renamed to src/Meet/Azure.hs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{-# LANGUAGE DataKinds #-}
22
{-# LANGUAGE DeriveGeneric #-}
33

4-
module Azure
4+
module Meet.Azure
55
( getToken,
66
getAvailabilityText,
77
fetchSchedules,
@@ -22,15 +22,15 @@ import qualified Data.Text.IO as T
2222
import Data.Time.Clock (UTCTime, addUTCTime, getCurrentTime)
2323
import Data.Time.Format.ISO8601 (iso8601Show)
2424
import qualified Data.Vector as V
25-
import Entities (Availability (..), HasSchedule, Minutes (..), Person (..), Room (..), Schedule (..))
2625
import GHC.Generics
26+
import Meet.Entities (Availability (..), HasSchedule, Minutes (..), Person (..), Room (..), Schedule (..))
27+
import Meet.Print (prettyThrow)
2728
import Network.HTTP.Req
28-
import Print (prettyThrow)
2929
import System.Directory (XdgDirectory (..), createDirectoryIfMissing, doesFileExist, getXdgDirectory)
3030
import System.Exit (ExitCode (..))
3131
import System.FilePath (takeDirectory)
32-
import System.Process (readProcessWithExitCode, spawnCommand)
3332
import System.Posix.Files (setFileMode)
33+
import System.Process (readProcessWithExitCode, spawnCommand)
3434

3535
data Token = Token
3636
{ accessToken :: Text,

src/Entities.hs renamed to src/Meet/Entities.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module Entities
1+
module Meet.Entities
22
( Availability (..),
33
Schedule (..),
44
HasSchedule,

src/Meetings.hs renamed to src/Meet/Meetings.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
module Meetings (Meeting (..), chooseBestMeeting, getMeetings, getRoomMeetings) where
1+
module Meet.Meetings (Meeting (..), chooseBestMeeting, getMeetings, getRoomMeetings) where
22

3-
import Entities (Minutes (..))
43
import Data.Foldable1 (maximumBy)
54
import Data.List (findIndices, transpose)
65
import Data.List.NonEmpty (NonEmpty)
@@ -21,7 +20,7 @@ import Data.Time.LocalTime
2120
zonedTimeToLocalTime,
2221
zonedTimeToUTC,
2322
)
24-
import Entities (Availability (..), Person (..), Room (..), Schedule (..))
23+
import Meet.Entities (Availability (..), Minutes (..), Person (..), Room (..), Schedule (..))
2524

2625
data RelativeMeeting = RelativeMeeting
2726
{ startIndex :: Int,

0 commit comments

Comments
 (0)