From 3b4abcc79ca8293b1df09efbe8a0a0356e245f0c Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Mon, 7 Feb 2022 09:24:43 +0000 Subject: [PATCH] Add Buf_read.parse_exn --- lib_eio/buf_read.ml | 5 +++++ lib_eio/eio.mli | 7 ++++++ tests/buf_reader.md | 53 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/lib_eio/buf_read.ml b/lib_eio/buf_read.ml index fccf66f..8b2f2fa 100644 --- a/lib_eio/buf_read.ml +++ b/lib_eio/buf_read.ml @@ -278,6 +278,11 @@ let parse ?initial_size ~max_size p flow = let buf = of_flow flow ?initial_size ~max_size in format_errors (p <* end_of_input) buf +let parse_exn ?initial_size ~max_size p flow = + match parse ?initial_size ~max_size p flow with + | Ok x -> x + | Error (`Msg m) -> failwith m + [@@inline never] let bad_offset ~expected actual = Fmt.invalid_arg "Sequence is stale (expected to be used at offset %d, but stream is now at %d)" diff --git a/lib_eio/eio.mli b/lib_eio/eio.mli index 0b508a1..14a8b61 100644 --- a/lib_eio/eio.mli +++ b/lib_eio/eio.mli @@ -628,6 +628,13 @@ module Buf_read : sig @param initial_size see {!of_flow}. *) + val parse_exn : ?initial_size:int -> max_size:int -> 'a parser -> #Flow.source -> 'a + (** [parse_exn] wraps {!parse}, but raises [Failure msg] if that returns [Error (`Msg msg)]. + + Catching exceptions with [parse] and then raising them might seem pointless, + but this has the effect of turning e.g. an [End_of_file] exception into a [Failure] + with a more user-friendly message. *) + val of_flow : ?initial_size:int -> max_size:int -> #Flow.source -> t (** [of_flow ~max_size flow] is a buffered reader backed by [flow]. diff --git a/tests/buf_reader.md b/tests/buf_reader.md index 8871d30..195d8ad 100644 --- a/tests/buf_reader.md +++ b/tests/buf_reader.md @@ -51,6 +51,11 @@ let test ?(max_size=10) input p = next := input; let i = R.of_flow mock_flow ~max_size in p i + +let parse_exn p flow ~max_size = + match R.parse_exn p flow ~max_size with + | x -> traceln "Ok: %S" x + | exception Failure msg -> traceln "Failure: %s" msg ``` @@ -511,3 +516,51 @@ Invalid_argument "Sequence is stale (expected to be used at offset 4, but stream is now at 8)". ``` +## Convenience wrapper + +`parse` turns parser errors into friendly messages: + +```ocaml +# R.(parse (string "FROM:" *> take_all)) (Eio.Flow.string_source "FROM:A") ~max_size:5;; +- : (string, [> `Msg of string ]) result = Ok "A" + +# R.(parse (string "FROM:" *> take_all)) (Eio.Flow.string_source "TO:B") ~max_size:5;; +- : (string, [> `Msg of string ]) result = +Error (`Msg "Expected \"FROM:\" but got \"TO:B\" (at offset 0)") + +# R.(parse (string "FROM:" *> take_all)) (Eio.Flow.string_source "FROM:ABCDE") ~max_size:5;; +- : (string, [> `Msg of string ]) result = +Error (`Msg "Buffer size limit exceeded when reading at offset 5") + +# R.(parse (string "END")) (Eio.Flow.string_source "ENDING") ~max_size:5;; +- : (unit, [> `Msg of string ]) result = +Error (`Msg "Unexpected data after parsing (at offset 3)") + +# R.(parse (string "END")) (Eio.Flow.string_source "E") ~max_size:5;; +- : (unit, [> `Msg of string ]) result = +Error (`Msg "Unexpected end-of-file at offset 1") +``` + +`parse_exn` is similar, but raises (we then catch it and print it nicely): + +```ocaml +# parse_exn R.(string "FROM:" *> take_all) (Eio.Flow.string_source "FROM:A") ~max_size:5;; ++Ok: "A" +- : unit = () + +# parse_exn R.(string "FROM:" *> take_all) (Eio.Flow.string_source "TO:B") ~max_size:5;; ++Failure: Expected "FROM:" but got "TO:B" (at offset 0) +- : unit = () + +# parse_exn R.(string "FROM:" *> take_all) (Eio.Flow.string_source "FROM:ABCDE") ~max_size:5;; ++Failure: Buffer size limit exceeded when reading at offset 5 +- : unit = () + +# parse_exn R.(take 3) (Eio.Flow.string_source "ENDING") ~max_size:5;; ++Failure: Unexpected data after parsing (at offset 3) +- : unit = () + +# parse_exn R.(take 3) (Eio.Flow.string_source "E") ~max_size:5;; ++Failure: Unexpected end-of-file at offset 1 +- : unit = () +```