@@ -17,7 +17,7 @@ Imagine you have the following configuration:
17
17
type Options =
18
18
'[ "appname" ::: Text
19
19
, "db" ::<
20
- '[ "username" ::: Text
20
+ '[ "username" ::: Text
21
21
, "password" ::: Text
22
22
]
23
23
]
@@ -38,8 +38,6 @@ module Loot.Config.Env
38
38
-- * Parsing individual values
39
39
, FromEnv (.. )
40
40
, Parser
41
- , noValue
42
- , withPresent
43
41
, parseStringEnvValue
44
42
, autoParseEnvValue
45
43
, parseBoundedNumEnvValue
@@ -77,7 +75,7 @@ import Loot.Config.Record ((::+), (::-), (:::), (::<), ConfigKind (Partial), Con
77
75
-- | A complete description of parsing error.
78
76
data EnvParseError = EnvParseError
79
77
{ errKey :: Text
80
- , errValue :: Maybe String
78
+ , errValue :: String
81
79
, errMessage :: Text
82
80
} deriving (Show , Eq )
83
81
@@ -90,8 +88,7 @@ instance Buildable EnvParseError where
90
88
-- My idea is to add "isSecure" flag to 'EnvValue' typelass to resolve
91
89
-- this problem.
92
90
" Failed to parse an environment variable \
93
- \" +| errKey|+ " =" +| maybe " -" build errValue|+ " \
94
- \: " +| errMessage|+ " "
91
+ \" +| errKey|+ " =" +| errValue|+ " : " +| errMessage|+ " "
95
92
96
93
-- | Pretty-print a 'EnvParseError'.
97
94
parseErrorPretty :: EnvParseError -> String
@@ -102,42 +99,27 @@ instance Exception EnvParseError where
102
99
103
100
-- | Parser for an environment variable.
104
101
--
105
- -- Use 'fail' to report parsing errors, and 'noValue' to indicate
106
- -- that value is left uninitialized.
102
+ -- Use 'fail' to report parsing errors.
107
103
newtype Parser a = Parser
108
- { runParser :: MaybeT (Except Text ) a
109
- -- ^ Parsing result, Maybe layer designates result presence, while Except
110
- -- layer contains parsing errors.
104
+ { runParser :: Except Text a
105
+ -- ^ Parsing result, Except layer contains parsing errors.
111
106
} deriving (Functor , Applicative , Monad )
112
107
113
108
instance MonadFail Parser where
114
- fail = Parser . lift . throwError . fromString
115
-
116
- -- | Leave value uninitialized in config.
117
- noValue :: Parser a
118
- noValue = Parser mzero
109
+ fail = Parser . throwError . fromString
119
110
120
111
-- | Describes a way to parse an item appearing in config.
121
112
class FromEnv a where
122
113
-- | Parse a variable value.
123
- parseEnvValue :: Maybe String -> Parser a
124
-
125
- -- | Apply the given parser to an environment value if it present,
126
- -- otherwise leave the configuration option uninitialized.
127
- --
128
- -- This is what most parsers usually do.
129
- withPresent :: (String -> Parser a ) -> Maybe String -> Parser a
130
- withPresent parser = \ case
131
- Nothing -> noValue
132
- Just val -> parser val
114
+ parseEnvValue :: String -> Parser a
133
115
134
116
-- | Value parser based on 'IsString' instance.
135
- parseStringEnvValue :: IsString a => Maybe String -> Parser a
136
- parseStringEnvValue = withPresent $ pure . fromString
117
+ parseStringEnvValue :: IsString a => String -> Parser a
118
+ parseStringEnvValue = pure . fromString
137
119
138
120
-- | Value parser based on 'Read' instance.
139
- autoParseEnvValue :: Read a => Maybe String -> Parser a
140
- autoParseEnvValue = withPresent $ \ arg -> case reads arg of
121
+ autoParseEnvValue :: Read a => String -> Parser a
122
+ autoParseEnvValue arg = case reads arg of
141
123
-- This is similar to what e.g. optparse-applicative does,
142
124
-- the errors from 'eitherRead' are not too beautiful
143
125
[(r, " " )] -> return r
@@ -148,17 +130,17 @@ autoParseEnvValue = withPresent $ \arg -> case reads arg of
148
130
-- | Value parser for numbers with overflow checks.
149
131
parseBoundedNumEnvValue
150
132
:: forall a . (Bounded a , Integral a )
151
- => Maybe String -> Parser a
133
+ => String -> Parser a
152
134
parseBoundedNumEnvValue val = do
153
135
int <- autoParseEnvValue @ Integer val
154
136
if | int < fromIntegral (minBound @ a ) -> fail " Numeric underflow"
155
137
| int > fromIntegral (maxBound @ a ) -> fail " Numeric overflow"
156
138
| otherwise -> pure (fromIntegral int)
157
139
158
140
-- | Value parser based on 'Aeson.FromJSON' instance.
159
- aesonParseEnvValue :: Aeson. FromJSON a => Maybe String -> Parser a
141
+ aesonParseEnvValue :: Aeson. FromJSON a => String -> Parser a
160
142
aesonParseEnvValue =
161
- withPresent $ either fail pure . Aeson. eitherDecode . encodeUtf8
143
+ either fail pure . Aeson. eitherDecode . encodeUtf8
162
144
163
145
-- | Options which define the expected format of environment variables.
164
146
data ParseOptions = ParseOptions
@@ -221,12 +203,12 @@ instance
221
203
(:&) <$> fmap ItemOptionP parseOption <*> envParser options env path
222
204
where
223
205
parseOption :: Either EnvParseError (Maybe v )
224
- parseOption =
206
+ parseOption = do
225
207
let key = mkEnvKey @ l options path
226
- mvalue = Map. lookup key env
227
- in first ( EnvParseError key mvalue) $
228
- runExcept . runMaybeT $
229
- runParser ( parseEnvValue mvalue)
208
+ let mvalue = Map. lookup key env
209
+ forM mvalue $ \ value ->
210
+ first ( EnvParseError key value) $
211
+ runExcept . runParser $ parseEnvValue value
230
212
231
213
gatherRequired options path _ =
232
214
Endo (mkEnvKey @ l options path : ) <>
@@ -383,21 +365,12 @@ instance Fixed.HasResolution a => FromEnv (Fixed.Fixed a) where
383
365
parseEnvValue = autoParseEnvValue
384
366
385
367
instance FromEnv Bool where
386
- parseEnvValue = withPresent $ \ case
368
+ parseEnvValue = \ case
387
369
" 0" -> pure False
388
370
" 1" -> pure True
389
371
(map toLower -> " false" ) -> pure False
390
372
(map toLower -> " true" ) -> pure True
391
373
_ -> fail " Invalid boolean"
392
374
393
- -- | Parses to @Nothing@ when value is not provided.
394
- -- Never leaves config value uninitialized.
395
- --
396
- -- Note that if env variable is defined but empty, it will be parsed anyway.
397
- instance FromEnv a => FromEnv (Maybe a ) where
398
- parseEnvValue = \ case
399
- Nothing -> pure Nothing
400
- Just val -> parseEnvValue (Just val)
401
-
402
375
instance FromEnv Aeson. Value where
403
376
parseEnvValue = aesonParseEnvValue
0 commit comments