diff --git a/src/Yaml/Decode.elm b/src/Yaml/Decode.elm index cc9af22..08ea766 100644 --- a/src/Yaml/Decode.elm +++ b/src/Yaml/Decode.elm @@ -1,6 +1,6 @@ module Yaml.Decode exposing ( Decoder - , fromString, Value, Error(..), fromValue, errorToString + , fromString, fromStringWithParserError, Value, Error(..), fromValue, errorToString , string, bool, int, float, null , nullable, list, dict , field, at @@ -30,7 +30,7 @@ to `Json.Decode`, so if you haven't worked with decoders before, reading through # Run Decoders -@docs fromString, Value, Error, fromValue, errorToString +@docs fromString, fromStringWithParserError, Value, Error, fromValue, errorToString # Primitives @@ -70,6 +70,7 @@ to `Json.Decode`, so if you haven't worked with decoders before, reading through -} import Dict +import Parser import Yaml.Parser as Yaml import Yaml.Parser.Ast as Ast @@ -82,7 +83,7 @@ for a more comprehensive introduction! -} type Decoder a - = Decoder (Yaml.Value -> Result Error a) + = Decoder (Yaml.Value -> Result String a) @@ -97,8 +98,8 @@ type alias Value = {-| A structured error describing how a decoder failed. -} -type Error - = Parsing String +type Error e + = Parsing e | Decoding String @@ -107,12 +108,14 @@ provided `Decoder`. This will fail if the string is not well-formed YAML or if the `Decoder` doesn't match the input. +If you want access to the raw `Parser.DeadEnd`s, use `fromStringWithParserError`. + fromString int "4" --> Ok 4 fromString int "hello" --> Err (Decoding "Expected int, got: \"hello\" (string)") -} -fromString : Decoder a -> String -> Result Error a +fromString : Decoder a -> String -> Result (Error String) a fromString decoder raw = case Yaml.fromString raw of Ok v -> @@ -122,16 +125,40 @@ fromString decoder raw = Err (Parsing error) +{-| Decode a given string into an Elm value based on the +provided `Decoder`. This will fail if the string is not +well-formed YAML or if the `Decoder` doesn't match the +input. + +This is similar to `fromString`, but it returns the raw `Parser.DeadEnd`s. + + fromStringWithParserError int "4" --> Ok 4 + + fromStringWithParserError int "\"" --> Err (Parsing [ ... ]) + +-} +fromStringWithParserError : Decoder a -> String -> Result (Error (List Parser.DeadEnd)) a +fromStringWithParserError (Decoder decoder) raw = + case Yaml.parse raw of + Ok v -> + decoder v + |> Result.mapError Decoding + + Err error -> + Err (Parsing error) + + {-| Run a `Decoder` on a Yaml `Value`. -} -fromValue : Decoder a -> Value -> Result Error a +fromValue : Decoder a -> Value -> Result (Error String) a fromValue (Decoder decoder) v = decoder v + |> Result.mapError Decoding {-| Convert a structured error into a `String` that is nice for debugging. -} -errorToString : Error -> String +errorToString : Error String -> String errorToString e = case e of Parsing msg -> @@ -321,7 +348,7 @@ null = -} nullable : Decoder a -> Decoder (Maybe a) -nullable decoder = +nullable (Decoder decoder) = Decoder <| \v -> case v of @@ -329,7 +356,7 @@ nullable decoder = Ok Nothing other -> - Result.map Just (fromValue decoder other) + Result.map Just (decoder other) {-| Decode a YAML array into an Elm `List`. @@ -343,12 +370,12 @@ nullable decoder = -} list : Decoder a -> Decoder (List a) -list decoder = +list (Decoder decoder) = Decoder <| \v -> case v of Ast.List_ list_ -> - singleResult (List.map (fromValue decoder) list_) + singleResult (List.map decoder list_) Ast.Null_ -> Ok [] @@ -369,17 +396,16 @@ list decoder = -} dict : Decoder a -> Decoder (Dict.Dict String a) -dict decoder = +dict (Decoder decoder) = Decoder <| \v -> case v of Ast.Record_ properties -> properties |> Dict.toList - |> List.map (\( key, val ) -> ( key, fromValue decoder val )) |> List.filterMap (\( key, val ) -> - case val of + case decoder val of Ok val_ -> Just ( key, val_ ) @@ -525,7 +551,7 @@ fail : String -> Decoder a fail error = Decoder <| \_ -> - Err (Decoding error) + Err error {-| Create decoders that depend on previous results. @@ -573,12 +599,16 @@ field: -} andThen : (a -> Decoder b) -> Decoder a -> Decoder b -andThen next decoder = +andThen next (Decoder decoder) = Decoder <| \v0 -> - case fromValue decoder v0 of + case decoder v0 of Ok a -> - fromValue (next a) v0 + let + (Decoder nextDecoder) = + next a + in + nextDecoder v0 Err err -> Err err @@ -648,15 +678,15 @@ oneOf ds = {-| Choose between (try out) two decoders. -} or : Decoder a -> Decoder a -> Decoder a -or lp rp = +or (Decoder lp) (Decoder rp) = Decoder <| \v -> - case fromValue lp v of + case lp v of Ok a -> Ok a Err _ -> - fromValue rp v + rp v @@ -1020,7 +1050,7 @@ fromMaybe err mby = -- INTERNAL -singleResult : List (Result Error a) -> Result Error (List a) +singleResult : List (Result e a) -> Result e (List a) singleResult = let each : Result error value -> Result error (List value) -> Result error (List value) @@ -1040,33 +1070,31 @@ singleResult = List.foldl each (Ok []) >> Result.map List.reverse -find : List String -> Decoder a -> Ast.Value -> Result Error a -find names decoder v0 = +find : List String -> Decoder a -> Ast.Value -> Result String a +find names (Decoder decoder) v0 = case names of name :: rest -> case v0 of Ast.Record_ properties -> case Dict.get name properties of Just v1 -> - find rest decoder v1 + find rest (Decoder decoder) v1 Nothing -> - Err (Decoding <| "Expected property: " ++ name) + Err ("Expected property: " ++ name) _ -> - Err (Decoding "Expected record") + Err "Expected record" [] -> - fromValue decoder v0 + decoder v0 -decodeError : String -> Ast.Value -> Result Error a +decodeError : String -> Ast.Value -> Result String a decodeError expected got = Err - (Decoding - ("Expected " - ++ expected - ++ ", got: " - ++ Ast.toString got - ) + ("Expected " + ++ expected + ++ ", got: " + ++ Ast.toString got ) diff --git a/src/Yaml/Parser.elm b/src/Yaml/Parser.elm index e432483..3cd11c1 100644 --- a/src/Yaml/Parser.elm +++ b/src/Yaml/Parser.elm @@ -1,4 +1,4 @@ -module Yaml.Parser exposing (Value, fromString, parser) +module Yaml.Parser exposing (Value, fromString, parse, parser) import Dict exposing (Dict) import Parser as P exposing ((|.), (|=)) @@ -90,6 +90,13 @@ fromString = P.run parser >> Result.mapError deadEndsToString >> Result.map deref +{-| -} +parse : String -> Result (List P.DeadEnd) Ast.Value +parse input = + P.run parser input + |> Result.map deref + + {-| -} parser : P.Parser Ast.Value parser = diff --git a/tests/Expectations.elm b/tests/Expectations.elm index 9eae68d..53aa45c 100644 --- a/tests/Expectations.elm +++ b/tests/Expectations.elm @@ -18,7 +18,7 @@ expectCloseTo expected got = {-| Utility function that checks the failure mode of a Decoder -} -expectFail : String -> Result Decode.Error a -> Expect.Expectation +expectFail : String -> Result (Decode.Error e) a -> Expect.Expectation expectFail expected got = Expect.equal (Err (Decode.Decoding expected)) got diff --git a/tests/TestDecoder.elm b/tests/TestDecoder.elm index e9a229a..329fba2 100644 --- a/tests/TestDecoder.elm +++ b/tests/TestDecoder.elm @@ -401,6 +401,6 @@ suite = {-| Utility function that sets up a test. -} -given : String -> Yaml.Decoder a -> Result Yaml.Error a +given : String -> Yaml.Decoder a -> Result (Yaml.Error String) a given input decoder = Yaml.fromString decoder input