Skip to content

Commit 9f0b49b

Browse files
committed
Add supports for warnings while parsing
1 parent 4b44a92 commit 9f0b49b

File tree

7 files changed

+49
-16
lines changed

7 files changed

+49
-16
lines changed

src/Data/Aeson/Config/FromValue.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module Data.Aeson.Config.FromValue (
1212
FromValue(..)
1313
, Parser
1414
, Result
15+
, Warning(..)
16+
, WarningReason(..)
1517
, decodeValue
1618

1719
, Generic
@@ -28,6 +30,8 @@ module Data.Aeson.Config.FromValue (
2830
, withNumber
2931
, withBool
3032

33+
, warn
34+
3135
, parseArray
3236
, traverseObject
3337

@@ -56,7 +60,7 @@ import Data.Aeson.Types (FromJSON(..))
5660
import Data.Aeson.Config.Util
5761
import Data.Aeson.Config.Parser
5862

59-
type Result a = Either String (a, [String])
63+
type Result a = Either String (a, [Warning])
6064

6165
decodeValue :: FromValue a => Value -> Result a
6266
decodeValue = runParser fromValue

src/Data/Aeson/Config/Parser.hs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
{-# LANGUAGE CPP #-}
55
module Data.Aeson.Config.Parser (
66
Parser
7+
, Warning(..)
8+
, WarningReason(..)
79
, runParser
810

911
, typeMismatch
@@ -14,6 +16,8 @@ module Data.Aeson.Config.Parser (
1416
, withNumber
1517
, withBool
1618

19+
, warn
20+
1721
, explicitParseField
1822
, explicitParseFieldMaybe
1923

@@ -67,6 +71,7 @@ fromAesonPathElement e = case e of
6771

6872
data ParserState = ParserState {
6973
parserStateConsumedFields :: !(Set JSONPath)
74+
, parserStateWarnings :: ![(JSONPath, String)]
7075
}
7176

7277
newtype Parser a = Parser {unParser :: StateT ParserState Aeson.Parser a}
@@ -75,10 +80,22 @@ newtype Parser a = Parser {unParser :: StateT ParserState Aeson.Parser a}
7580
liftParser :: Aeson.Parser a -> Parser a
7681
liftParser = Parser . lift
7782

78-
runParser :: (Value -> Parser a) -> Value -> Either String (a, [String])
79-
runParser p v = case iparse (flip runStateT (ParserState mempty) . unParser <$> p) v of
83+
data Warning = Warning String WarningReason
84+
deriving (Eq, Show)
85+
86+
data WarningReason = WarningReason String | UnknownField
87+
deriving (Eq, Show)
88+
89+
runParser :: (Value -> Parser a) -> Value -> Either String (a, [Warning])
90+
runParser p v = case iparse (flip runStateT (ParserState mempty mempty) . unParser <$> p) v of
8091
IError path err -> Left ("Error while parsing " ++ formatPath (fromAesonPath path) ++ " - " ++ err)
81-
ISuccess (a, ParserState consumed) -> Right (a, map formatPath (determineUnconsumed consumed v))
92+
ISuccess (a, ParserState consumed warnings) -> Right (a, map warning warnings ++ map unknownField (determineUnconsumed consumed v))
93+
where
94+
warning :: (JSONPath, String) -> Warning
95+
warning (path, reason) = Warning (formatPath path) (WarningReason reason)
96+
97+
unknownField :: JSONPath -> Warning
98+
unknownField path = Warning (formatPath path) UnknownField
8299

83100
formatPath :: JSONPath -> String
84101
formatPath = go "$" . reverse
@@ -160,3 +177,8 @@ withNumber _ v = typeMismatch "Number" v
160177
withBool :: (Bool -> Parser a) -> Value -> Parser a
161178
withBool p (Bool b) = p b
162179
withBool _ v = typeMismatch "Boolean" v
180+
181+
warn :: String -> Parser ()
182+
warn s = do
183+
path <- getPath
184+
Parser . modify $ \ st -> st {parserStateWarnings = (path, s) : parserStateWarnings st}

src/Hpack/Config.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,16 +849,18 @@ sectionAll f sect = f sect <> foldMap (foldMap $ sectionAll f) (sectionCondition
849849

850850
decodeValue :: FromValue a => ProgramName -> FilePath -> Value -> Warnings (Errors IO) a
851851
decodeValue (ProgramName programName) file value = do
852-
(r, unknown) <- lift . ExceptT . return $ first (prefix ++) (Config.decodeValue value)
852+
(r, warnings) <- lift . ExceptT . return $ first (prefix ++) (Config.decodeValue value)
853853
case r of
854854
UnsupportedSpecVersion v -> do
855855
lift $ throwE ("The file " ++ file ++ " requires version " ++ showVersion v ++ " of the Hpack package specification, however this version of " ++ programName ++ " only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of " ++ programName ++ " may resolve this issue.")
856856
SupportedSpecVersion a -> do
857-
tell (map formatUnknownField unknown)
857+
tell (map formatWarning warnings)
858858
return a
859859
where
860860
prefix = file ++ ": "
861-
formatUnknownField name = prefix ++ "Ignoring unrecognized field " ++ name
861+
formatWarning warning = prefix ++ case warning of
862+
Warning path (WarningReason reason) -> reason <> " in " <> path
863+
Warning path UnknownField -> "Ignoring unrecognized field " <> path
862864

863865
data CheckSpecVersion a = SupportedSpecVersion a | UnsupportedSpecVersion Version
864866

src/Hpack/Yaml.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ module Hpack.Yaml (
2020
import Data.Bifunctor
2121
import Data.Yaml hiding (decodeFile, decodeFileWithWarnings)
2222
import Data.Yaml.Include
23-
import Data.Yaml.Internal (Warning(..))
23+
import qualified Data.Yaml.Internal as Yaml
2424
import Data.Aeson.Config.FromValue
2525
import Data.Aeson.Config.Parser (fromAesonPath, formatPath)
2626

27-
formatWarning :: FilePath -> Warning -> String
27+
formatWarning :: FilePath -> Yaml.Warning -> String
2828
formatWarning file = \ case
29-
DuplicateKey path -> file ++ ": Duplicate field " ++ formatPath (fromAesonPath path)
29+
Yaml.DuplicateKey path -> file ++ ": Duplicate field " ++ formatPath (fromAesonPath path)
3030

3131
decodeYaml :: FilePath -> IO (Either String ([String], Value))
3232
decodeYaml file = do

test/Data/Aeson/Config/FromValueSpec.hs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ spec = do
5252
name: "Joe"
5353
age: 23
5454
foo: bar
55-
|] `shouldDecodeTo` Right (Person "Joe" 23 Nothing, ["$.foo"])
55+
|] `shouldDecodeTo` Right (Person "Joe" 23 Nothing, [unknownField "$.foo"])
5656

5757
it "captures nested unrecognized fields" $ do
5858
[yaml|
@@ -63,7 +63,7 @@ spec = do
6363
zip: "123456"
6464
foo:
6565
bar: 23
66-
|] `shouldDecodeTo` Right (Person "Joe" 23 (Just (Address "somewhere" "123456")), ["$.address.foo"])
66+
|] `shouldDecodeTo` Right (Person "Joe" 23 (Just (Address "somewhere" "123456")), [unknownField "$.address.foo"])
6767

6868
it "ignores fields that start with an underscore" $ do
6969
[yaml|
@@ -95,7 +95,7 @@ spec = do
9595
role: engineer
9696
salary: 100000
9797
foo: bar
98-
|] `shouldDecodeTo` Right ((Person "Joe" 23 Nothing, Job "engineer" 100000), ["$.foo"])
98+
|] `shouldDecodeTo` Right ((Person "Joe" 23 Nothing, Job "engineer" 100000), [unknownField "$.foo"])
9999

100100
context "with []" $ do
101101
it "captures unrecognized fields" $ do
@@ -111,7 +111,7 @@ spec = do
111111
- name: "Marry"
112112
age: 25
113113
bar: 42
114-
|] `shouldDecodeTo` Right (expected, ["$[1].bar", "$[0].address.foo"])
114+
|] `shouldDecodeTo` Right (expected, [unknownField "$[1].bar", unknownField "$[0].address.foo"])
115115

116116
context "with Map" $ do
117117
it "captures unrecognized fields" $ do
@@ -120,4 +120,4 @@ spec = do
120120
region: somewhere
121121
zip: '123456'
122122
foo: bar
123-
|] `shouldDecodeTo` Right (Map.fromList [("Joe", Address "somewhere" "123456")], ["$.Joe.foo"])
123+
|] `shouldDecodeTo` Right (Map.fromList [("Joe", Address "somewhere" "123456")], [unknownField "$.Joe.foo"])

test/Helper.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Helper (
1010
, module System.FilePath
1111
, withCurrentDirectory
1212
, yaml
13+
, unknownField
1314
) where
1415

1516
import Test.Hspec
@@ -26,6 +27,7 @@ import Data.Yaml.TH (yamlQQ)
2627
import Language.Haskell.TH.Quote (QuasiQuoter)
2728

2829
import Hpack.Config
30+
import Data.Aeson.Config.FromValue
2931

3032
instance IsString Cond where
3133
fromString = CondExpression
@@ -42,3 +44,6 @@ withTempDirectory action = Temp.withSystemTempDirectory "hspec" $ \dir -> do
4244

4345
yaml :: Language.Haskell.TH.Quote.QuasiQuoter
4446
yaml = yamlQQ
47+
48+
unknownField :: String -> Warning
49+
unknownField path = Warning path UnknownField

test/Hpack/Syntax/DependenciesSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ spec = do
212212
outer-name:
213213
name: inner-name
214214
path: somewhere
215-
|] `shouldDecodeTo` Right (Dependencies [("outer-name", defaultInfo { dependencyInfoVersion = DependencyVersion source AnyVersion })], ["$.outer-name.name"])
215+
|] `shouldDecodeTo` Right (Dependencies [("outer-name", defaultInfo { dependencyInfoVersion = DependencyVersion source AnyVersion })], [unknownField "$.outer-name.name"])
216216

217217
it "defaults to any version" $ do
218218
[yaml|

0 commit comments

Comments
 (0)