Más contenido relacionado La actualidad más candente (20) Similar a Haskell で CLI (20) Más de Nobutada Matsubara (20) Haskell で CLI8. System.Console.GetOpt
これも base にある
getArgs で得た文字列を特定の型に変換してくれる.
getOpt
:: ArgOrder a -- ^ non-opts な文字列の取り扱い方
-> [OptDescr a] -- ^ opts な文字列の扱い方
-> [String] -- ^ getArgs などで得た文字列
-> ([a], [String], [String])
返り値の型は (オプションの型, non-opts な文字列, エラー)
(細かい説明は割愛)
9. System.Console.GetOpt の使用例
import System.Console.GetOpt
main :: IO ()
main = do
args <- getArgs
let (opts, rest, err) = getOpt Permute options args
print opts
data Flag = Verbose | Version deriving (Show, Eq)
options :: [OptDescr Flag]
options =
[ Option ['v'] ["verbose"] (NoArg Verbose) "..."
, Option ['V'] ["version"] (NoArg Version) "..."
]
11. optparse-applicative の使用例
import Options.Applicative
main :: IO ()
main = print =<< execParser options
where
opts = sample `withInfo` "Print a greeting for TARGET"
data Sample = Sample { hello :: String, verbose :: Bool }
sample :: Parser Sample
sample = Sample
<$> strOption ( long "hello" <> help ".." )
<*> switch ( long "verbose" <> short 'v' <> help ".." )
withInfo :: Parser a -> String -> ParserInfo a
withInfo opts = info (helper <*> opts) . progDesc
13. optparse-applicative でサブコマンド
サブコマンドのパーサー
subcmdParser :: Parser SubCmd
subcmdParser = subparser
$ command "new" (pure NewCmd `withInfo` "...")
<> command "add" (AddCmd <$> ... `withInfo` "...")
<> command "done" (DoneCmd <$> ... `withInfo` "...")
<> command "list" (pure ListCmd `withInfo` "...")
hoge new とか hoge add ってサブコマンドになる
Semigroup の結合( <> )しかしてないんで網羅性チェッ
クは無理
16. 拡張可能バリアントの導入
variantFrom ::
Forall (KeyIs KnownSymbol) xs =>
RecordOf ParserInfo xs -> Parser (Variant xs)
variantFrom = subparser . subcmdVariant
where
subcmdVariant =
hfoldMapWithIndexFor (Proxy @ (KeyIs KnownSymbol))
$ m x ->
let k = symbolVal (proxyAssocKey m)
in command k
((EmbedAt m . Field . pure) <$> getField x)
instance Wrapper ParserInfo where
type Repr ParserInfo a = ParserInfo a
_Wrapper = id
21. RIO ライブラリの使用例
run :: RIO Env () -> Options -> IO ()
run cmd opts = do
token <- liftIO $ getEnv "MEDIUM_TOKEN"
logOpts <- logOptionsHandle stdout (opts ^. #verbose)
withLogFunc logOpts $ logger -> do
let env = #logger @= logger
<: #token @= fromString token
<: nil
runRIO env cmd
callMeAPI :: RIO Env ()
callMeAPI = do
logDebug "Run cmd: call me api"
token <- asks (view #token)
user <- API.getMe token
logDebug $ display ("get: " <> tshow user)
logInfo $ display ("Hi " <> user ^. #name <> "!!")
22. 実行してみる
$ mdium --version
Version 0.2.0.0, Git revision
efcf1856b6b065141a6d366663b97b0301053ffd (23 commits)
$ mdium --verbose --me
2018-11-10 10:28:53.049919: [debug] Run cmd: call me api
@(src/Mdium/Cmd/Run.hs:57:3)
2018-11-10 10:28:54.721968: [debug] get: id @= "..." <:
username @= "nobutada" <: name @= "matsubara" <: url @=
"https://medium.com/@nobutada" <: imageUrl @= ".." <: nil
@(src/Mdium/Cmd/Run.hs:60:3)
2018-11-10 10:28:54.722133: [info] Hi matsubara!!
@(src/Mdium/Cmd/Run.hs:61:3)
25. stack new の後がパターン化
stack new した後に各基本的なコードが同じ
よく使うパッケージを package.yaml に追加
コマンドライン引数の処理する部分を追加
などなど
これを「オレオレテンプレート化」できる
26. 自作テンプレートの例 (hsfiles)
{-# START_FILE package.yaml #-}
name: {{name}}
version: 0.1.0.0
homepage: https://github.com/{{github-username}}/{{name}}
...
dependencies:
- base >= 4.7 && < 5
- rio >= 0.1.1.0
- extensible >= 0.4.9
- yaml
...
{-# START_FILE app/Main.hs #-}
module Main where
import RIO
import Data.Extensible
...