Skip to content

Commit

Permalink
updating to using optparse-applicative
Browse files Browse the repository at this point in the history
  • Loading branch information
travgm committed Nov 6, 2024
1 parent 7fa1fc8 commit d838786
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 60 deletions.
3 changes: 2 additions & 1 deletion Lib/DecimalTime.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Types
DecimalTime (..),
Seconds (..),
ValidDecimalTime (..),
(><),
currentDate,
decimalTime,
extendedFlag)
Expand Down Expand Up @@ -72,7 +73,7 @@ timeOfDayToFraction = Days . (/ secondsInDay) . (\(Seconds s) -> s) . timeOfDayT
{-# INLINEABLE mkValidDecimalTime #-}
mkValidDecimalTime :: DecimalTime -> Either String ValidDecimalTime
mkValidDecimalTime dt@(DecimalTime t)
| t >= 0 && t <= 1000 = Right $ ValidDecimalTime dt
| t >< 1000 = Right $ ValidDecimalTime dt
| otherwise = Left "Time must be between 0 and 1000"

-- | Convert fraction of day to decimal time
Expand Down
15 changes: 6 additions & 9 deletions Lib/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ module Types (
, currentDate
, Lens
, Lens''
, RunMode(..)
, Config(..)
, (><)
) where

import Data.Time (LocalTime)
import qualified Control.Lens as L

-- | Infix operator for range checking where lower bound is 0
infixr 5 ><
(><) :: Integer -> Integer -> Bool
(><) x y = x >= 0 && x <= y

-- | Decimal time types
newtype Seconds = Seconds Double
deriving (Eq, Ord, Num, Fractional, Real, RealFrac)
Expand All @@ -44,13 +48,6 @@ newtype DecimalTime = DecimalTime Integer
newtype ValidDecimalTime = ValidDecimalTime DecimalTime
deriving (Show, Eq)

data RunMode = SingleRun | Watch

data Config = Config
{ extended :: Bool
, mode :: RunMode
}

data ClockState = ClockState
{ _extendedFlag :: Bool,
_decimalTime :: Maybe ValidDecimalTime,
Expand Down
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ Usage
You can type -v or --version to see program information or -e to print extended information which is the current date. If you want to leave it running for a realtime decimal clock use the -w option.

```
$ cabal run
Decimal time: 292
$
```
or if you have copied the "dclock" executable to a bin directory
$ cabal run exe:dclock -- -h
dclock - decimal time clock
```
$ dclock
Decimal time: 999
$ dclock -v
Decimal time clock that maps your day to 1000 decimal minutes, version 1.0.0 (x86_64-linux)
$ dclock -e
Decimal time: 49 (2024-10-30)
Usage: dclock [(-v|--version) | [-e|--extended] [-w|--watch]]
Decimal time clock that maps your day to 1000 decimal minutes
Available options:
-v,--version Show version information
-e,--extended Show extended information including date
-w,--watch Watch mode, view as a realtime decimal clock (updates
every second)
-h,--help Show this help text
$ cabal run
Decimal time: 25
$ cabal run exe:dclock -- -e
Decimal time: 25 (2024-11-05)
$
```
Building
Expand Down
110 changes: 72 additions & 38 deletions app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
-----------------------------------------------------------------------------
-- |
Expand Down Expand Up @@ -27,19 +26,54 @@ import Data.Time (ZonedTime, getZonedTime)
import Control.Monad.IO.Class (liftIO)
import Control.Concurrent (threadDelay)
import System.Info (arch, os)
import System.Environment(getArgs)
import Data.Function(fix)
import Options.Applicative
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified PrettyPrinter as Pretty (formatTime, displaySingleLine)
import qualified DecimalTime as DT (
localTimeToDecimal,
setCurrentDate)
import Types (
ClockState( .. ),
Config(..),
RunMode(SingleRun,
Watch))
import Types (ClockState( .. ))

data RunMode = SingleRun | Watch

data Config = Config
{ extended :: Bool
, mode :: RunMode
}

data Command
= Version
| Run Config

-- | Parser for command line arguments
parser :: Parser Command
parser = versionCmd <|> runCmd
where
versionCmd =
flag'
Version
( long "version"
<> short 'v'
<> help "Show version information"
)

runCmd =
fmap Run $
Config
<$> switch
( long "extended"
<> short 'e'
<> help "Show extended information including date"
)
<*> flag
SingleRun
Watch
( long "watch"
<> short 'w'
<> help "Watch mode, view as a realtime decimal clock (updates every second)"
)

-- | Get platform information for version string
createPlatformText :: T.Text
Expand Down Expand Up @@ -68,37 +102,37 @@ zonedTime = construct $ do
zt <- liftIO getZonedTime
yield zt

-- | Process args and run the clock
runClockProcess :: [String] -> IO ()
runClockProcess = \case
[] -> runWith $ Config False SingleRun
["-e"] -> runWith $ Config True SingleRun
["-w"] -> runWith $ Config False Watch
["-w", "-e"] -> runWith $ Config True Watch
["-e", "-w"] -> runWith $ Config True Watch
["-v"] -> displayVersionText
["--version"] -> displayVersionText
_ -> displayValidArgs
main :: IO ()
main = execParser opts >>= run
where
runWith :: Config -> IO ()
runWith config = case mode config of
SingleRun -> runClock (extended config) >> TIO.putStrLn ""
Watch -> watchClock (extended config)
opts =
info
(parser <**> helper)
( fullDesc
<> progDesc "Decimal time clock that maps your day to 1000 decimal minutes"
<> header "dclock - decimal time clock"
)

run :: Command -> IO ()
run Version = displayVersionText
run (Run config) = runWith config
where
runWith :: Config -> IO ()
runWith config' = case mode config' of
SingleRun -> runClock (extended config') >> TIO.putStrLn ""
Watch -> watchClock (extended config')

runClock :: Bool -> IO ()
runClock e = do
let state = ClockState e Nothing Nothing
runT_ $
zonedTime
~> M.mapping (`DT.setCurrentDate` state)
~> M.mapping DT.localTimeToDecimal
~> M.mapping Pretty.formatTime
~> displayTimeText
runClock :: Bool -> IO ()
runClock e = do
let state = ClockState e Nothing Nothing
runT_ $
zonedTime
~> M.mapping (`DT.setCurrentDate` state)
~> M.mapping DT.localTimeToDecimal
~> M.mapping Pretty.formatTime
~> displayTimeText

watchClock :: Bool -> IO ()
watchClock extended' =
TIO.putStrLn "Press Ctrl-C to exit\n" >>
fix (\loop -> runClock extended' >> threadDelay 1000000 >> loop)

main :: IO ()
main = getArgs >>= runClockProcess
watchClock :: Bool -> IO ()
watchClock extended' =
TIO.putStrLn "Press Ctrl-C to exit\n" >>
fix (\loop -> runClock extended' >> threadDelay 1000000 >> loop)
1 change: 1 addition & 0 deletions dclock.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ executable dclock
time ^>=1.14,
machines ^>= 0.7.2,
text ^>= 2.1.1,
optparse-applicative ^>= 0.18.1
hs-source-dirs: app
default-language: Haskell2010

Expand Down

0 comments on commit d838786

Please sign in to comment.