multihash-0.1.0: main/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Crypto.Hash (Digest)
import qualified Crypto.Hash as CH
import Data.Byteable (toBytes)
import Data.ByteString (ByteString)
import Data.ByteString.Lazy (fromStrict, toStrict)
import Options.Applicative
import System.Exit (exitFailure, exitSuccess)
import System.IO.Streams (InputStream, stdin, stdout,
withFileAsInput, write)
import System.IO.Streams.Crypto (hashInputStream)
import qualified Data.Multihash.Base as MB
import qualified Data.Multihash.Digest as MH
data Termination = Null | Newline deriving (Show, Eq)
data Config =
Config
{ cfFile :: Maybe FilePath
, cfAlgo :: MH.HashAlgorithm
, cfBase :: MB.BaseEncoding
, cfHash :: Maybe MH.Digest
, cfTerm :: Termination
} deriving Show
main :: IO ()
main = do
-- TODO add file checking
config <- execParser opts
digest <- maybe (hashStdin config) (hashFile config) $ cfFile config
write (multihash config digest) stdout
where
hashStdin config = hash (cfAlgo config) stdin
hashFile config file = withFileAsInput file . hash $ cfAlgo config
multihash (Config _file algo base _hash term) =
Just . toStrict . line term . MB.encode base . MH.encode algo
line Null = (<> "\0")
line Newline = (<> "\n")
-- TODO add BLAKE support
hash :: MH.HashAlgorithm -> InputStream ByteString -> IO MH.Digest
hash MH.SHA1 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA1))
hash MH.SHA256 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA256))
hash MH.SHA512 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA512))
hash MH.SHA3 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA3_256))
hash MH.BLAKE2B _ = undefined
hash MH.BLAKE2S _ = undefined
opts :: ParserInfo Config
opts = info
(helper <*> (Config
<$> fileArg
<*> algoOpt
<*> baseOpt
<*> checkOpt
<*> nullTermFlag
))
(fullDesc
<> header "Generate a multihash for the given input."
<> progDesc "Hash from FILE or stdin if not given.")
algoOpt :: Parser MH.HashAlgorithm
algoOpt =
option auto
$ long "algorithm"
<> short 'a'
<> metavar "ALGO"
<> showDefault <> value MH.SHA256
<> help ("Hash algorithm to apply to input, ignored if checking hash " <> show ([minBound..] :: [MH.HashAlgorithm]))
baseOpt :: Parser MB.BaseEncoding
baseOpt =
option auto
$ long "encoding"
<> short 'e'
<> metavar "ENCODING"
<> showDefault <> value MB.Base58
<> help ("Base encoding of output digest, ignored if checking hash " <> show ([minBound..] :: [MB.BaseEncoding]))
checkOpt :: Parser (Maybe MH.Digest)
checkOpt =
optional . option auto
$ long "check"
<> short 'c'
<> metavar "DIGEST"
<> help "Check for matching digest"
nullTermFlag :: Parser Termination
nullTermFlag =
flag Newline Null
$ long "print0"
<> short '0'
<> help "End filenames with NUL, for use with xargs"
fileArg :: Parser (Maybe FilePath)
fileArg = optional . argument str $ metavar "FILE"