{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverlappingInstances  #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeSynonymInstances #-}

{-# OPTIONS_GHC -fno-warn-deprecated-flags #-}

module WithCli.Argument where

import           Data.Orphans ()
import           Prelude ()
import           Prelude.Compat

import           Data.List
import           Data.Proxy
import           Text.Read

-- | 'Argument' is a typeclass for things that can be parsed as atomic values from
--   single command line arguments, e.g. strings (and filenames) and numbers.
--
--   Occasionally you might want to declare your own instance for additional
--   type safety and for providing a more informative command argument type.
--   Here's an example:

-- ### Start "docs/CustomOption.hs" "module CustomOption where\n\n" Haddock ###

-- |
-- >  {-# LANGUAGE DeriveDataTypeable #-}
-- >
-- >  import WithCli
-- >
-- >  data File = File FilePath
-- >    deriving (Show, Typeable)
-- >
-- >  instance Argument File where
-- >    argumentType Proxy = "custom-file-type"
-- >    parseArgument f = Just (File f)
-- >
-- >  instance HasArguments File where
-- >    argumentsParser = atomicArgumentsParser
-- >
-- >  main :: IO ()
-- >  main = withCli run
-- >
-- >  run :: File -> IO ()
-- >  run = print

-- ### End ###

-- | And this is how the above program behaves:

-- ### Start "docs/CustomOption.shell-protocol" "" Haddock ###

-- |
-- >  $ program --help
-- >  program [OPTIONS] custom-file-type
-- >    -h  --help  show help and exit
-- >  $ program some/file
-- >  File "some/file"

-- ### End ###

class Argument a where
  argumentType :: Proxy a -> String
  parseArgument :: String -> Maybe a

instance Argument String where
  argumentType :: Proxy String -> String
argumentType Proxy String
Proxy = String
"STRING"
  parseArgument :: String -> Maybe String
parseArgument = forall a. a -> Maybe a
Just

instance Argument Int where
  argumentType :: Proxy Int -> String
argumentType Proxy Int
_ = String
"INTEGER"
  parseArgument :: String -> Maybe Int
parseArgument = forall a. Read a => String -> Maybe a
readMaybe

instance Argument Integer where
  argumentType :: Proxy Integer -> String
argumentType Proxy Integer
_ = String
"INTEGER"
  parseArgument :: String -> Maybe Integer
parseArgument = forall a. Read a => String -> Maybe a
readMaybe

instance Argument Float where
  argumentType :: Proxy Float -> String
argumentType Proxy Float
_ = String
"NUMBER"
  parseArgument :: String -> Maybe Float
parseArgument = forall n. (RealFloat n, Read n) => String -> Maybe n
readFloat

instance Argument Double where
  argumentType :: Proxy Double -> String
argumentType Proxy Double
_ = String
"NUMBER"
  parseArgument :: String -> Maybe Double
parseArgument = forall n. (RealFloat n, Read n) => String -> Maybe n
readFloat

readFloat :: (RealFloat n, Read n) => String -> Maybe n
readFloat :: forall n. (RealFloat n, Read n) => String -> Maybe n
readFloat String
s = case forall a. Read a => String -> Maybe a
readMaybe String
s of
  Just n
n -> forall a. a -> Maybe a
Just n
n
  Maybe n
Nothing
    | String
"." forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s -> forall a. Read a => String -> Maybe a
readMaybe (String
"0" forall a. [a] -> [a] -> [a]
++ String
s)
    | Bool
otherwise -> forall a. Maybe a
Nothing