diff options
| author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-03-01 07:34:23 +0100 |
|---|---|---|
| committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-03-01 07:34:23 +0100 |
| commit | 61c983bd242ec45726ce572e85c069ca9274c71e (patch) | |
| tree | 2bd74a9c80dd4f5c9e8a061894958cd9d19680b4 | |
| parent | fc83a7241e27574ef2bb81de206c59a9a854efe6 (diff) | |
| download | thermoprint-61c983bd242ec45726ce572e85c069ca9274c71e.tar thermoprint-61c983bd242ec45726ce572e85c069ca9274c71e.tar.gz thermoprint-61c983bd242ec45726ce572e85c069ca9274c71e.tar.bz2 thermoprint-61c983bd242ec45726ce572e85c069ca9274c71e.tar.xz thermoprint-61c983bd242ec45726ce572e85c069ca9274c71e.zip | |
Cleanup
| -rw-r--r-- | tprint/src/Instances.hs | 25 | ||||
| -rw-r--r-- | tprint/src/Main.hs | 155 | ||||
| -rw-r--r-- | tprint/src/Options.hs | 115 | ||||
| -rw-r--r-- | tprint/src/Options/Utils.hs | 48 | ||||
| -rw-r--r-- | tprint/tprint.cabal | 6 |
5 files changed, 201 insertions, 148 deletions
diff --git a/tprint/src/Instances.hs b/tprint/src/Instances.hs new file mode 100644 index 0000000..cffb8b0 --- /dev/null +++ b/tprint/src/Instances.hs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | module Instances where | ||
| 2 | |||
| 3 | import Data.Text (Text) | ||
| 4 | import qualified Data.Text as T (unpack) | ||
| 5 | |||
| 6 | import Data.Time (UTCTime, formatTime, defaultTimeLocale) | ||
| 7 | |||
| 8 | import Text.Show.Pretty (Value, PrettyVal(..), dumpStr) | ||
| 9 | import qualified Text.Show.Pretty as PShow (Value(..)) | ||
| 10 | |||
| 11 | import Thermoprint.Client (Scheme(..), BaseUrl(..), PrinterId(..), JobId(..), DraftId(..), Range(..)) | ||
| 12 | |||
| 13 | instance PrettyVal Scheme | ||
| 14 | instance PrettyVal BaseUrl | ||
| 15 | instance PrettyVal PrinterId | ||
| 16 | instance PrettyVal JobId | ||
| 17 | instance PrettyVal DraftId | ||
| 18 | instance PrettyVal a => PrettyVal (Range a) | ||
| 19 | |||
| 20 | instance PrettyVal UTCTime where | ||
| 21 | prettyVal = PShow.String . formatTime defaultTimeLocale "%Z %F %X" | ||
| 22 | |||
| 23 | instance PrettyVal Text where | ||
| 24 | prettyVal = prettyVal . T.unpack | ||
| 25 | |||
diff --git a/tprint/src/Main.hs b/tprint/src/Main.hs index e8d4670..cd6e68b 100644 --- a/tprint/src/Main.hs +++ b/tprint/src/Main.hs | |||
| @@ -1,6 +1,4 @@ | |||
| 1 | {-# LANGUAGE RecordWildCards #-} | 1 | {-# LANGUAGE RecordWildCards #-} |
| 2 | {-# LANGUAGE StandaloneDeriving #-} | ||
| 3 | {-# LANGUAGE DeriveGeneric, DeriveAnyClass #-} | ||
| 4 | 2 | ||
| 5 | import Data.Map (Map) | 3 | import Data.Map (Map) |
| 6 | import qualified Data.Map as Map | 4 | import qualified Data.Map as Map |
| @@ -8,157 +6,22 @@ import Data.Sequence (Seq) | |||
| 8 | import qualified Data.Sequence as Seq | 6 | import qualified Data.Sequence as Seq |
| 9 | import Data.Text (Text) | 7 | import Data.Text (Text) |
| 10 | import qualified Data.Text as T | 8 | import qualified Data.Text as T |
| 11 | import Data.Time | ||
| 12 | |||
| 13 | import Data.Foldable | ||
| 14 | import Data.Char | ||
| 15 | import Text.Read | ||
| 16 | import Data.Maybe | ||
| 17 | import Data.List | ||
| 18 | 9 | ||
| 19 | import GHC.Generics (Generic) | 10 | import Control.Monad |
| 20 | 11 | ||
| 21 | import Text.Show.Pretty (Value, PrettyVal(..), dumpStr) | 12 | import Text.Show.Pretty (dumpStr) |
| 22 | import qualified Text.Show.Pretty as PShow (Value(..)) | 13 | |
| 14 | import System.IO | ||
| 23 | 15 | ||
| 24 | import Thermoprint.Client | 16 | import Thermoprint.Client |
| 25 | import Options.Applicative | 17 | import Options |
| 26 | 18 | ||
| 27 | import Debug.Trace | 19 | import Debug.Trace |
| 28 | 20 | ||
| 29 | instance PrettyVal Scheme | ||
| 30 | instance PrettyVal BaseUrl | ||
| 31 | instance PrettyVal PrinterId | ||
| 32 | instance PrettyVal JobId | ||
| 33 | instance PrettyVal DraftId | ||
| 34 | instance PrettyVal a => PrettyVal (Range a) | ||
| 35 | |||
| 36 | instance PrettyVal UTCTime where | ||
| 37 | prettyVal = PShow.String . formatTime defaultTimeLocale "%Z %F %X" | ||
| 38 | |||
| 39 | instance PrettyVal Text where | ||
| 40 | prettyVal = prettyVal . T.unpack | ||
| 41 | |||
| 42 | data TPrint = TPrint | ||
| 43 | { baseUrl :: BaseUrl | ||
| 44 | , dryRun :: Bool | ||
| 45 | , output :: Output | ||
| 46 | , operation :: Operation | ||
| 47 | } | ||
| 48 | deriving (Show, Generic, PrettyVal) | ||
| 49 | |||
| 50 | data Operation | ||
| 51 | = Printers | ||
| 52 | | Jobs | ||
| 53 | { printer :: Maybe PrinterId | ||
| 54 | , jobRange :: Maybe (Range JobId) | ||
| 55 | , timeRange :: Maybe (Range UTCTime) | ||
| 56 | } | ||
| 57 | | JobCreate | ||
| 58 | { printer :: Maybe PrinterId | ||
| 59 | , input :: Input | ||
| 60 | } | ||
| 61 | | Job { jobId :: JobId } | ||
| 62 | | JobStatus { jobId :: JobId } | ||
| 63 | | JobDelete { jobId :: JobId } | ||
| 64 | | Drafts | ||
| 65 | | DraftCreate | ||
| 66 | { draftTitle :: Maybe DraftTitle | ||
| 67 | , input :: Input | ||
| 68 | } | ||
| 69 | | DraftReplace | ||
| 70 | { draftId :: DraftId | ||
| 71 | , draftTitle :: Maybe DraftTitle | ||
| 72 | , input :: Input | ||
| 73 | } | ||
| 74 | | Draft { draftId :: DraftId } | ||
| 75 | | DraftDelete { draftId :: DraftId } | ||
| 76 | | DraftPrint | ||
| 77 | { draftId :: DraftId | ||
| 78 | , printer :: Maybe PrinterId | ||
| 79 | } | ||
| 80 | deriving (Show, Generic, PrettyVal) | ||
| 81 | |||
| 82 | type Input = (Format, Source) | ||
| 83 | type Output = (Format, Sink ) | ||
| 84 | |||
| 85 | data Format = Human | BBCode | Internal | JSON | ||
| 86 | deriving (Enum, Bounded, Show, Read, Generic, PrettyVal) | ||
| 87 | |||
| 88 | data Source = Stdin | ReadFile FilePath | ||
| 89 | deriving (Show, Generic, PrettyVal) | ||
| 90 | |||
| 91 | data Sink = Stdout | WriteFile FilePath | ||
| 92 | deriving (Show, Generic, PrettyVal) | ||
| 93 | |||
| 94 | cmdPrinters :: ParserInfo Operation | ||
| 95 | cmdPrinters = info cmdPrinters' $ mconcat [ header "List all available printers" | ||
| 96 | ] | ||
| 97 | where cmdPrinters' = pure Printers | ||
| 98 | |||
| 99 | cmdJobs :: ParserInfo Operation | ||
| 100 | cmdJobs = info cmdJobs' $ mconcat [ header "List printjobs" | ||
| 101 | ] | ||
| 102 | where | ||
| 103 | cmdJobs' = Jobs | ||
| 104 | <$> optional (option (PrinterId <$> auto) $ metavar "PRINTER" <> long "printer" <> short 'p' <> help "List only jobs associated with printer #PRINTER") | ||
| 105 | <*> pRange (JobId <$> auto) (metavar "JOB") (long "min-job" <> short 'j' <> help "List only jobs with id greater than or equal to JOB") (long "max-job" <> short 'J' <> help "List only jobs with id less than or equal to JOB") | ||
| 106 | <*> pRange rTime (metavar "TIME") (long "min-time" <> short 't' <> help "List only jobs created after or at TIME") (long "max-time" <> short 'T' <> help "List only jobs created before or at TIME") | ||
| 107 | |||
| 108 | pOperation :: Parser Operation | ||
| 109 | pOperation = hsubparser $ mconcat [ command "printers" cmdPrinters | ||
| 110 | , command "jobs" cmdJobs | ||
| 111 | ] | ||
| 112 | |||
| 113 | pTPrint :: Parser TPrint | ||
| 114 | pTPrint = TPrint <$> option (eitherReader parseBaseUrl) (metavar "URL" <> long "baseurl" <> short 'u' <> help "Server to interact with" <> value (BaseUrl Http "localhost" 3000) <> showDefaultWith showBaseUrl) | ||
| 115 | <*> switch (long "dry-run" <> short 'n' <> help "Don't send any requests that would be expected to change the servers state" <> showDefault) | ||
| 116 | <*> pOutput | ||
| 117 | <*> pOperation | ||
| 118 | |||
| 119 | pOutput :: Parser Output | ||
| 120 | pOutput = (,) <$> pOutputFormat <*> pSink | ||
| 121 | where | ||
| 122 | pOutputFormat = option ciAuto $ metavar "FORMAT" <> long "output-format" <> short 'O' <> help ("Format to use for all output (possible values: " ++ show ([minBound..maxBound] :: [Format]) ++ ")") <> value Human <> showDefault | ||
| 123 | pSink = option rSink $ metavar "PATH" <> long "output" <> short 'o' <> help "Send output to PATH (`-' means stdout)" <> value Stdout <> showDefault | ||
| 124 | rSink = rSink' <$> str | ||
| 125 | where | ||
| 126 | rSink' "-" = Stdout | ||
| 127 | rSink' x = WriteFile x | ||
| 128 | |||
| 129 | ciAuto :: (Read a, Show a) => ReadM a | ||
| 130 | ciAuto = eitherReader rRep' | ||
| 131 | where | ||
| 132 | rRep' str = case mapMaybe readMaybe $ cases str of | ||
| 133 | [] -> Left $ "Could not parse `" ++ str ++ "'" | ||
| 134 | [x] -> Right x | ||
| 135 | xs -> Left $ "Ambiguous parse for `" ++ str ++ "': " ++ show xs | ||
| 136 | cases [] = [] | ||
| 137 | cases (c:cs) = [(c':cs') | c' <- [toLower c, toUpper c], cs' <- cases cs] | ||
| 138 | |||
| 139 | rTime :: ReadM UTCTime | ||
| 140 | rTime = eitherReader rTime' | ||
| 141 | where | ||
| 142 | rTime' str = maybe (Left $ "Could not parse `" ++ str ++ "' as a specification of time -- try the output of `date -u +'%Y-%m-%d %H:%M:%S' \"" ++ str ++ "\"`") Right | ||
| 143 | . foldr (<|>) Nothing | ||
| 144 | $ map (flip (parseTimeM True defaultTimeLocale) str) ( "%c" : "%s" : perms' [ [time, date, timezone] | time <- ["%T", "%X", "%I:%M:%S", "%r", "%X%Q", "%I:%M:%S%Q %p"], date <- ["%x", "%F"], timezone <- ["%z", "%Z"]] ) | ||
| 145 | perms' :: [[String]] -> [String] | ||
| 146 | perms' = map unwords . concatMap subsequences . concatMap permutations | ||
| 147 | |||
| 148 | pRange :: ReadM a | ||
| 149 | -> Mod OptionFields a -- ^ Modifiers applied to both min & max | ||
| 150 | -> Mod OptionFields a -- ^ Modifiers for minimum | ||
| 151 | -> Mod OptionFields a -- ^ Modifiers for maximum | ||
| 152 | -> Parser (Maybe (Range a)) | ||
| 153 | pRange r both min max = toRange <$> optional (option r (both <> min)) <*> optional (option r (both <> max)) | ||
| 154 | where | ||
| 155 | toRange Nothing Nothing = Nothing | ||
| 156 | toRange (Just min) Nothing = Just $ Min min | ||
| 157 | toRange Nothing (Just max) = Just $ Max max | ||
| 158 | toRange (Just min) (Just max) = Just $ min `Through` max | ||
| 159 | |||
| 160 | main :: IO () | 21 | main :: IO () |
| 161 | main = customExecParser (prefs $ showHelpOnError) (info pTPrint $ header "tprint - A cli interface to Thermoprint.Client") >>= tprint | 22 | main = withArgs (tprint <=< dumpOpts) |
| 23 | where | ||
| 24 | dumpOpts c@(TPrint{..}) = c <$ when (dumpOptions) (hPutStrLn stderr $ dumpStr c) | ||
| 162 | 25 | ||
| 163 | tprint :: TPrint -> IO () | 26 | tprint :: TPrint -> IO () |
| 164 | tprint = putStrLn . dumpStr -- *DEBUG* | 27 | tprint = undefined |
diff --git a/tprint/src/Options.hs b/tprint/src/Options.hs new file mode 100644 index 0000000..96bbde6 --- /dev/null +++ b/tprint/src/Options.hs | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | {-# LANGUAGE StandaloneDeriving #-} | ||
| 2 | {-# LANGUAGE DeriveGeneric, DeriveAnyClass #-} | ||
| 3 | |||
| 4 | module Options | ||
| 5 | ( TPrint(..) | ||
| 6 | , Operation(..) | ||
| 7 | , Input, Output | ||
| 8 | , Format(..), Source(..), Sink(..) | ||
| 9 | , withArgs | ||
| 10 | , module Options.Utils | ||
| 11 | ) where | ||
| 12 | |||
| 13 | import Data.Time | ||
| 14 | |||
| 15 | import GHC.Generics (Generic) | ||
| 16 | |||
| 17 | import Text.Show.Pretty (PrettyVal) | ||
| 18 | |||
| 19 | import Thermoprint.Client | ||
| 20 | import Options.Applicative | ||
| 21 | |||
| 22 | import Options.Utils | ||
| 23 | import Instances () | ||
| 24 | |||
| 25 | data TPrint = TPrint | ||
| 26 | { baseUrl :: BaseUrl | ||
| 27 | , dryRun :: Bool | ||
| 28 | , output :: Output | ||
| 29 | , operation :: Operation | ||
| 30 | , dumpOptions :: Bool | ||
| 31 | } | ||
| 32 | deriving (Show, Generic, PrettyVal) | ||
| 33 | |||
| 34 | data Operation | ||
| 35 | = Printers | ||
| 36 | | Jobs | ||
| 37 | { printer :: Maybe PrinterId | ||
| 38 | , jobRange :: Maybe (Range JobId) | ||
| 39 | , timeRange :: Maybe (Range UTCTime) | ||
| 40 | } | ||
| 41 | | JobCreate | ||
| 42 | { printer :: Maybe PrinterId | ||
| 43 | , input :: Input | ||
| 44 | } | ||
| 45 | | Job { jobId :: JobId } | ||
| 46 | | JobStatus { jobId :: JobId } | ||
| 47 | | JobDelete { jobId :: JobId } | ||
| 48 | | Drafts | ||
| 49 | | DraftCreate | ||
| 50 | { draftTitle :: Maybe DraftTitle | ||
| 51 | , input :: Input | ||
| 52 | } | ||
| 53 | | DraftReplace | ||
| 54 | { draftId :: DraftId | ||
| 55 | , draftTitle :: Maybe DraftTitle | ||
| 56 | , input :: Input | ||
| 57 | } | ||
| 58 | | Draft { draftId :: DraftId } | ||
| 59 | | DraftDelete { draftId :: DraftId } | ||
| 60 | | DraftPrint | ||
| 61 | { draftId :: DraftId | ||
| 62 | , printer :: Maybe PrinterId | ||
| 63 | } | ||
| 64 | deriving (Show, Generic, PrettyVal) | ||
| 65 | |||
| 66 | type Input = (Format, Source) | ||
| 67 | type Output = (Format, Sink ) | ||
| 68 | |||
| 69 | data Format = Human | BBCode | Internal | JSON | ||
| 70 | deriving (Enum, Bounded, Show, Read, Generic, PrettyVal) | ||
| 71 | |||
| 72 | data Source = Stdin | ReadFile FilePath | ||
| 73 | deriving (Show, Generic, PrettyVal) | ||
| 74 | |||
| 75 | data Sink = Stdout | WriteFile FilePath | ||
| 76 | deriving (Show, Generic, PrettyVal) | ||
| 77 | |||
| 78 | cmdPrinters :: ParserInfo Operation | ||
| 79 | cmdPrinters = info cmdPrinters' $ mconcat [ header "List all available printers" | ||
| 80 | ] | ||
| 81 | where cmdPrinters' = pure Printers | ||
| 82 | |||
| 83 | cmdJobs :: ParserInfo Operation | ||
| 84 | cmdJobs = info cmdJobs' $ mconcat [ header "List printjobs" | ||
| 85 | ] | ||
| 86 | where | ||
| 87 | cmdJobs' = Jobs | ||
| 88 | <$> optional (option (PrinterId <$> auto) $ metavar "PRINTER" <> long "printer" <> short 'p' <> help "List only jobs associated with printer #PRINTER") | ||
| 89 | <*> pRange (JobId <$> auto) (metavar "JOB") (long "min-job" <> short 'j' <> help "List only jobs with id greater than or equal to JOB") (long "max-job" <> short 'J' <> help "List only jobs with id less than or equal to JOB") | ||
| 90 | <*> pRange rTime (metavar "TIME") (long "min-time" <> short 't' <> help "List only jobs created after or at TIME") (long "max-time" <> short 'T' <> help "List only jobs created before or at TIME") | ||
| 91 | |||
| 92 | pOperation :: Parser Operation | ||
| 93 | pOperation = hsubparser $ mconcat [ command "printers" cmdPrinters | ||
| 94 | , command "jobs" cmdJobs | ||
| 95 | ] | ||
| 96 | |||
| 97 | pTPrint :: Parser TPrint | ||
| 98 | pTPrint = TPrint <$> option (eitherReader parseBaseUrl) (metavar "URL" <> long "baseurl" <> short 'u' <> help "Server to interact with" <> value (BaseUrl Http "localhost" 3000) <> showDefaultWith showBaseUrl) | ||
| 99 | <*> switch (long "dry-run" <> short 'n' <> help "Don't send any requests that would be expected to change the servers state" <> showDefault) | ||
| 100 | <*> pOutput | ||
| 101 | <*> pOperation | ||
| 102 | <*> switch (long "dump-options" <> internal) | ||
| 103 | |||
| 104 | pOutput :: Parser Output | ||
| 105 | pOutput = (,) <$> pOutputFormat <*> pSink | ||
| 106 | where | ||
| 107 | pOutputFormat = option rCI $ metavar "FORMAT" <> long "output-format" <> short 'O' <> help ("Format to use for all output (possible values: " ++ show ([minBound..maxBound] :: [Format]) ++ ")") <> value Human <> showDefault | ||
| 108 | pSink = option rSink $ metavar "PATH" <> long "output" <> short 'o' <> help "Send output to PATH (`-' means stdout)" <> value Stdout <> showDefault | ||
| 109 | rSink = rSink' <$> str | ||
| 110 | where | ||
| 111 | rSink' "-" = Stdout | ||
| 112 | rSink' x = WriteFile x | ||
| 113 | |||
| 114 | withArgs :: (TPrint -> IO a) -> IO a | ||
| 115 | withArgs a = customExecParser (prefs $ showHelpOnError) (info pTPrint $ header "tprint - A cli interface to Thermoprint.Client") >>= a | ||
diff --git a/tprint/src/Options/Utils.hs b/tprint/src/Options/Utils.hs new file mode 100644 index 0000000..80b4a7e --- /dev/null +++ b/tprint/src/Options/Utils.hs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | module Options.Utils | ||
| 2 | ( rCI | ||
| 3 | , rTime | ||
| 4 | , pRange | ||
| 5 | ) where | ||
| 6 | |||
| 7 | import Options.Applicative | ||
| 8 | |||
| 9 | import Data.Char | ||
| 10 | import Data.Maybe | ||
| 11 | import Data.List | ||
| 12 | import Text.Read | ||
| 13 | |||
| 14 | import Data.Time | ||
| 15 | |||
| 16 | import Thermoprint.Client (Range(..)) | ||
| 17 | |||
| 18 | rCI :: (Read a, Show a) => ReadM a | ||
| 19 | rCI = eitherReader rRep' | ||
| 20 | where | ||
| 21 | rRep' str = case mapMaybe readMaybe $ cases str of | ||
| 22 | [] -> Left $ "Could not parse `" ++ str ++ "'" | ||
| 23 | [x] -> Right x | ||
| 24 | xs -> Left $ "Ambiguous parse for `" ++ str ++ "': " ++ show xs | ||
| 25 | cases [] = [] | ||
| 26 | cases (c:cs) = [(c':cs') | c' <- [toLower c, toUpper c], cs' <- cases cs] | ||
| 27 | |||
| 28 | rTime :: ReadM UTCTime | ||
| 29 | rTime = eitherReader rTime' | ||
| 30 | where | ||
| 31 | rTime' str = maybe (Left $ "Could not parse `" ++ str ++ "' as a specification of time -- try the output of `date -u +'%Y-%m-%d %H:%M:%S' \"" ++ str ++ "\"`") Right | ||
| 32 | . foldr (<|>) Nothing | ||
| 33 | $ map (flip (parseTimeM True defaultTimeLocale) str) ( "%c" : "%s" : perms' [ [time, date, timezone] | time <- ["%T", "%X", "%I:%M:%S", "%r", "%X%Q", "%I:%M:%S%Q %p"], date <- ["%x", "%F"], timezone <- ["%z", "%Z"]] ) | ||
| 34 | perms' :: [[String]] -> [String] | ||
| 35 | perms' = map unwords . concatMap subsequences . concatMap permutations | ||
| 36 | |||
| 37 | pRange :: ReadM a | ||
| 38 | -> Mod OptionFields a -- ^ Modifiers applied to both min & max | ||
| 39 | -> Mod OptionFields a -- ^ Modifiers for minimum | ||
| 40 | -> Mod OptionFields a -- ^ Modifiers for maximum | ||
| 41 | -> Parser (Maybe (Range a)) | ||
| 42 | pRange r both min max = toRange <$> optional (option r (both <> min)) <*> optional (option r (both <> max)) | ||
| 43 | where | ||
| 44 | toRange Nothing Nothing = Nothing | ||
| 45 | toRange (Just min) Nothing = Just $ Min min | ||
| 46 | toRange Nothing (Just max) = Just $ Max max | ||
| 47 | toRange (Just min) (Just max) = Just $ min `Through` max | ||
| 48 | |||
diff --git a/tprint/tprint.cabal b/tprint/tprint.cabal index d61ea01..aeb61c9 100644 --- a/tprint/tprint.cabal +++ b/tprint/tprint.cabal | |||
| @@ -18,7 +18,9 @@ cabal-version: >=1.10 | |||
| 18 | 18 | ||
| 19 | executable tprint | 19 | executable tprint |
| 20 | main-is: Main.hs | 20 | main-is: Main.hs |
| 21 | -- other-modules: | 21 | other-modules: Options |
| 22 | , Options.Utils | ||
| 23 | , Instances | ||
| 22 | -- other-extensions: | 24 | -- other-extensions: |
| 23 | build-depends: base >=4.8 && <4.9 | 25 | build-depends: base >=4.8 && <4.9 |
| 24 | , thermoprint-bbcode >=1.0.0 && <2 | 26 | , thermoprint-bbcode >=1.0.0 && <2 |
| @@ -27,7 +29,7 @@ executable tprint | |||
| 27 | , containers >=0.5.6 && <1 | 29 | , containers >=0.5.6 && <1 |
| 28 | , time >=1.5.0 && <2 | 30 | , time >=1.5.0 && <2 |
| 29 | , pretty-show >=1.6.9 && <2 | 31 | , pretty-show >=1.6.9 && <2 |
| 30 | , text -any | 32 | , text >=1.2.2 && <2 |
| 31 | hs-source-dirs: src | 33 | hs-source-dirs: src |
| 32 | default-language: Haskell2010 | 34 | default-language: Haskell2010 |
| 33 | ghc-options: -Wall \ No newline at end of file | 35 | ghc-options: -Wall \ No newline at end of file |
