diff --git a/Pentole/result.fs b/Pentole/result.fs index 46adaa5..5c0e698 100644 --- a/Pentole/result.fs +++ b/Pentole/result.fs @@ -1,12 +1,86 @@ -module Pentole.Result +namespace Pentole -type Result<'o, 'e> with - +module Result = + + let inline protect ([]f) x = + try + Ok (f x) + with e -> Error e + + let inline pairwise_map fun_ (x: 'a, y: 'a) = + match fun_ x with + | Error e -> Error e + | Ok o -> + match fun_ y with | Ok o' -> Ok (o, o') | Error e -> Error e + + let of_option = function | Some s -> Ok s | None -> Error () + + let zip a b = + match (a, b) with + | Ok a, Ok b -> Ok (a, b) + | Error e, _ -> Error e + | _, Error e -> Error e + + module Unsafe = + /// + /// `o` if `Ok o` else throw + /// + /// + let inline get r = + match r with + | Ok o -> o + | Error e -> + $"Tried to access value from Result r, error={e}" + |> System.ArgumentException + |> raise + +/// Module to operate on lists +module ResultList = /// - /// `o` if `Ok o` else throw + /// Transforms a sequence by applying a function that returns a Result, collecting successful results or short-circuiting on the first error. /// - /// - static member get (result: Result<'o, 'e>) = - match result with - | Ok o -> o - | Error e -> failwith $"Can't unwrap Error {e}" + /// + /// A function that transforms each element and returns a Result + /// The input sequence to transform + /// + /// + /// - Ok of a list containing all successfully transformed elements if all transformations succeed + /// + /// + /// - Error with the first encountered error if any transformation fails + /// + /// + /// + /// Basic usage: + /// + /// + /// let parseInts = ResultList.collect int "123,456,789".Split(',') + /// // Returns Ok [123; 456; 789] + /// + /// + /// + /// Error handling: + /// + /// let parseInts = ResultList.collect (Result.protect int) ["123"; "abc"; "456"] + /// // Returns Error (FormatException "The input string 'abc' was not in a correct format.") + /// + /// + /// + /// Custom transformation: + /// + /// + /// let validatePositive x = + /// if x > 0 then Ok x else Error "Negative value" + /// let result = ResultList.collect validatePositive [1; 2; -3; 4] + /// // Returns Error "Negative value" + /// + /// + let collect (lambda: 'a -> Result<'ok, 'err>) (seq_: 'a seq) = + let rec iter_ acc seq_ = + match Seq.tryHead seq_ with + | None -> Ok acc + | Some x -> + match lambda x with + | Error e -> Error e + | Ok o -> iter_ (o::acc) (Seq.tail seq_) + iter_ [] seq_ diff --git a/tests/path_tests.fs b/tests/path_tests.fs index 753e15c..989737e 100644 --- a/tests/path_tests.fs +++ b/tests/path_tests.fs @@ -2,9 +2,9 @@ module Tests.Path open NUnit.Framework +open Pentole open Pentole.TestsExtensions open Pentole.Path -open Pentole.Result [] let constructor_test () = @@ -42,8 +42,8 @@ let resolve_test () = s |> Path.of_string |> Result.map FileSystem.resolve - |> Result.get - let p (n: string ) = Path.of_string n |> Result.get + |> Result.Unsafe.get + let p (n: string ) = Path.of_string n |> Result.Unsafe.get "/" |> test |> Assert.ok_is_equal (p "/") "/etc/../" |> test |> Assert.ok_is_equal (p "/") @@ -51,7 +51,7 @@ let resolve_test () = [] let equality_test () = - let p (n: string ) = Path.of_string n |> Result.get + let p (n: string ) = Path.of_string n |> Result.Unsafe.get Assert.are_equal (p "/etc") (p "/etc/") (* [] diff --git a/tests/result_tests.fs b/tests/result_tests.fs new file mode 100644 index 0000000..d0655d8 --- /dev/null +++ b/tests/result_tests.fs @@ -0,0 +1,16 @@ +module Tests.Result + +open NUnit.Framework + +open Pentole.TestsExtensions + +open Pentole + +[] +let err_test () = + let got = ResultList.collect (Result.protect int) ["123"; "abc"; "456"] + let msg = "The input string 'abc' was not in a correct format." + + match got with + | Ok _ -> Assert.Fail "Expected an error" + | Error exn -> Assert.are_equal exn.Message msg diff --git a/tests/tests.fsproj b/tests/tests.fsproj index bb59764..971a5cc 100644 --- a/tests/tests.fsproj +++ b/tests/tests.fsproj @@ -10,6 +10,7 @@ +