/
Utils.hs
139 lines (121 loc) · 4.32 KB
/
Utils.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
#if !defined(mingw32_HOST_OS)
#define UNIX
#endif
-- | Internal utils for the other Api modules
--
module Cardano.Api.Utils
( (?!)
, (?!.)
, formatParsecError
, failEither
, failEitherWith
, noInlineMaybeToStrictMaybe
, note
, readFileBlocking
, renderEra
, runParsecParser
, textShow
, writeSecrets
-- ** CLI option parsing
, bounded
) where
import Control.Exception (bracket)
import Control.Monad (forM_, when)
import qualified Data.Aeson.Types as Aeson
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as Builder
import qualified Data.ByteString.Lazy as LBS
import Data.Maybe.Strict
import Data.Text (Text)
import qualified Data.Text as Text
import GHC.IO.Handle.FD (openFileBlocking)
import System.FilePath ((</>))
import System.IO (IOMode (ReadMode), hClose)
import qualified Text.Parsec as Parsec
import qualified Text.Parsec.String as Parsec
import qualified Text.ParserCombinators.Parsec.Error as Parsec
import Text.Printf (printf)
#ifdef UNIX
import System.Posix.Files (ownerReadMode, setFileMode)
#else
import System.Directory (emptyPermissions, readable, setPermissions)
#endif
import Cardano.Api.Eras
import Options.Applicative (ReadM)
import Options.Applicative.Builder (eitherReader)
import qualified Text.Read as Read
(?!) :: Maybe a -> e -> Either e a
Nothing ?! e = Left e
Just x ?! _ = Right x
(?!.) :: Either e a -> (e -> e') -> Either e' a
Left e ?!. f = Left (f e)
Right x ?!. _ = Right x
{-# NOINLINE noInlineMaybeToStrictMaybe #-}
noInlineMaybeToStrictMaybe :: Maybe a -> StrictMaybe a
noInlineMaybeToStrictMaybe Nothing = SNothing
noInlineMaybeToStrictMaybe (Just x) = SJust x
formatParsecError :: Parsec.ParseError -> String
formatParsecError err =
Parsec.showErrorMessages "or" "unknown parse error"
"expecting" "unexpected" "end of input"
$ Parsec.errorMessages err
runParsecParser :: Parsec.Parser a -> Text -> Aeson.Parser a
runParsecParser parser input =
case Parsec.parse (parser <* Parsec.eof) "" (Text.unpack input) of
Right txin -> pure txin
Left parseError -> fail $ formatParsecError parseError
failEither :: MonadFail m => Either String a -> m a
failEither = either fail pure
failEitherWith :: MonadFail m => (e -> String) -> Either e a -> m a
failEitherWith f = either (fail . f) pure
note :: MonadFail m => String -> Maybe a -> m a
note msg = \case
Nothing -> fail msg
Just a -> pure a
writeSecrets :: FilePath -> [Char] -> [Char] -> (a -> BS.ByteString) -> [a] -> IO ()
writeSecrets outDir prefix suffix secretOp xs =
forM_ (zip xs [0::Int ..]) $
\(secret, nr)-> do
let filename = outDir </> prefix <> "." <> printf "%03d" nr <> "." <> suffix
BS.writeFile filename $ secretOp secret
#ifdef UNIX
setFileMode filename ownerReadMode
#else
setPermissions filename (emptyPermissions {readable = True})
#endif
readFileBlocking :: FilePath -> IO BS.ByteString
readFileBlocking path = bracket
(openFileBlocking path ReadMode)
hClose
(\fp -> do
-- An arbitrary block size.
let blockSize = 4096
let go acc = do
next <- BS.hGet fp blockSize
if BS.null next
then pure acc
else go (acc <> Builder.byteString next)
contents <- go mempty
pure $ LBS.toStrict $ Builder.toLazyByteString contents)
textShow :: Show a => a -> Text
textShow = Text.pack . show
renderEra :: AnyCardanoEra -> Text
renderEra (AnyCardanoEra ByronEra) = "Byron"
renderEra (AnyCardanoEra ShelleyEra) = "Shelley"
renderEra (AnyCardanoEra AllegraEra) = "Allegra"
renderEra (AnyCardanoEra MaryEra) = "Mary"
renderEra (AnyCardanoEra AlonzoEra) = "Alonzo"
renderEra (AnyCardanoEra BabbageEra) = "Babbage"
renderEra (AnyCardanoEra ConwayEra) = "Conway"
bounded :: forall a. (Bounded a, Integral a, Show a) => String -> ReadM a
bounded t = eitherReader $ \s -> do
i <- Read.readEither @Integer s
when (i < fromIntegral (minBound @a)) $ Left $ t <> " must not be less than " <> show (minBound @a)
when (i > fromIntegral (maxBound @a)) $ Left $ t <> " must not greater than " <> show (maxBound @a)
pure (fromIntegral i)