Mhailist-0.0: LTR/Ttool.hs
{-# OPTIONS_GHC -XOverloadedStrings #-}
{-
Read or write a transaction to or from a disk file.
Usage:
ttool append TFILE APP TYPE (-d DATA | -f FILE) [-t TS]
ttool dump TFILE
ttool undump FILE
Commands:
append Append a transaction to a file. If the file does not
exist, it will be created.
dump Dump, in human-readable format, the transactions from
a transaction file.
undump Read dump output (though many input fields are optional)
and convert it to binary format, sending it to stdout.
Arguments:
TFILE a transaction log file.
APP application number (32-bit unsigned decimal)
TYPE type number (16-bit unsigned decimal)
Options:
Options may be interspersed anywhere within the command line.
Files that start with a hyphen should be specified as "./-name".
-t TS Specify a timestamp, in milliseconds since the epoch.
If not specified, the current time is used.
-d DATA Specify as a string data to be written to the file.
-f FILE Specify a file containing the transaction data.
Notes:
Application Numbers: 1 testing, 2 mhailist
Add an option for lock testing? This could write part of the
transaction, delay, and then write the other part. While that's
running in the background, you can run another one on the same
file in the foreground and ensure that a valid transaction log
is produced.
Until we get the originator stuff worked out, the originator is
always zero. Probably we would have some way of generating a
default originator (host info plus PID?) and a -o <originator>
option to let us override it.
We don't do stdin/out or network at the moment, because those can't
be locked. Should we try to add some support for this? Or just leave
that to other programs?
In the long run, we should probably add an interactive mode (-i)
which will let us do various kinds of manipulations.
-}
module LTR.Ttool (main) where
import Data.Binary (encode)
import Data.ByteString.Lazy.Char8 (pack)
import Data.Time.Clock (UTCTime, diffUTCTime)
import Data.Time.Format (readTime)
import LTR.TextTrans (transToText, textTransactionsParser)
import LTR.Transaction
import System.Console.GetOpt
import System.Environment (getArgs)
import System.Locale (defaultTimeLocale)
import Text.ParserCombinators.Parsec (parse)
import qualified Data.ByteString.Lazy as B
----------------------------------------------------------------------
data Command
= AppendCommand FilePath Int Int [Flag] -- file app type flags
| DumpCommand FilePath [Flag]
| UndumpCommand FilePath
| CommandError String
deriving Show
data Flag
= TransData String
| TransDataFile String
| TransTimestamp String
deriving (Show)
options :: [OptDescr Flag]
options =
[ Option ['d'] [] (ReqArg (\s -> TransData s) "DATA")
"transaction data"
, Option ['f'] [] (ReqArg (\s -> TransDataFile s) "FILE")
"FILE containing transaction data"
, Option ['t'] [] (ReqArg (\s -> TransTimestamp s) "TS")
"timestamp, in milliseconds since the epoch."
]
parseCommandLine :: [String] -> Command
parseCommandLine argv =
case getOpt Permute options argv of
(flags, "append":path:app:ttype:[], [])
-> AppendCommand path (read app) (read ttype) flags
(flags, "dump":path:[], []) -> DumpCommand path flags
(_, "undump":path:[], []) -> UndumpCommand path
err -> CommandError (show err)
append path app ttype writeData =
do
putStrLn $ "writing :" ++ show trans ++ "\n\n"
appendTransaction path trans
where
tempOriginator = mkOriginator 0
oneMinute = readTime defaultTimeLocale "%M" "01" :: UTCTime
epoch = readTime defaultTimeLocale "" "" :: UTCTime
tempTimeStamp = oneMinute `diffUTCTime` epoch
trans = mkTransaction
(fromIntegral app)
(fromIntegral ttype)
tempOriginator
tempTimeStamp
writeData
dump :: FilePath -> IO ()
dump path = getTransactions path >>= mapM_ (putStr.transToText)
undump :: FilePath -> IO ()
undump path =
do input <- readFile path
case parse textTransactionsParser path input of
Left err -> error $ show err
Right ts -> B.putStr $ B.concat $ map encode ts
usage :: String -> IO ()
usage msg =
error $ concat
[ "Invalid arguments: ", msg, "\n\n"
, "Usage: ttool append TFILE APP TYPE "
, "(-d DATA | -f FILE) [-t TS] \n"
, " ttool dump TFILE\n"
]
main :: IO ()
main =
do argv <- getArgs
case parseCommandLine argv of
CommandError s -> usage s
DumpCommand path _flags -> dump path
UndumpCommand path -> undump path
AppendCommand path app ttype
[TransData writeData] -> append path app ttype (pack writeData)
AppendCommand path app ttype
[TransDataFile file] -> B.readFile file >>=
append path app ttype
AppendCommand _ _ _ _ ->
error "No transaction data or file given"