Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 64 additions & 36 deletions src/Yaml/Decode.elm
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -82,7 +83,7 @@ for a more comprehensive introduction!

-}
type Decoder a
= Decoder (Yaml.Value -> Result Error a)
= Decoder (Yaml.Value -> Result String a)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like it should be another type parameter rather that a concrete String. What do you think?




Expand All @@ -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


Expand All @@ -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 ->
Expand All @@ -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 ->
Expand Down Expand Up @@ -321,15 +348,15 @@ null =

-}
nullable : Decoder a -> Decoder (Maybe a)
nullable decoder =
nullable (Decoder decoder) =
Decoder <|
\v ->
case v of
Ast.Null_ ->
Ok Nothing

other ->
Result.map Just (fromValue decoder other)
Result.map Just (decoder other)


{-| Decode a YAML array into an Elm `List`.
Expand All @@ -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 []
Expand All @@ -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_ )

Expand Down Expand Up @@ -525,7 +551,7 @@ fail : String -> Decoder a
fail error =
Decoder <|
\_ ->
Err (Decoding error)
Err error


{-| Create decoders that depend on previous results.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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



Expand Down Expand Up @@ -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)
Expand All @@ -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
)
9 changes: 8 additions & 1 deletion src/Yaml/Parser.elm
Original file line number Diff line number Diff line change
@@ -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 ((|.), (|=))
Expand Down Expand Up @@ -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 =
Expand Down
2 changes: 1 addition & 1 deletion tests/Expectations.elm
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/TestDecoder.elm
Original file line number Diff line number Diff line change
Expand Up @@ -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