From fc7a598a78f6f3cb73256372db9614dabd40b597 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Fri, 24 Mar 2023 12:02:00 -0700 Subject: [PATCH 1/3] Error.getUnsafe custom property --- src/Core__Error.res | 2 ++ src/Core__Error.resi | 24 ++++++++++++++++++++++++ test/ErrorTests.mjs | 37 +++++++++++++++++++++++++++++++++++++ test/ErrorTests.res | 30 ++++++++++++++++++++++++++++++ test/TestSuite.mjs | 3 +++ 5 files changed, 96 insertions(+) diff --git a/src/Core__Error.res b/src/Core__Error.res index 408b08ff..c66143d2 100644 --- a/src/Core__Error.res +++ b/src/Core__Error.res @@ -37,3 +37,5 @@ module URIError = { external raise: t => 'a = "%raise" let panic = msg => make(`Panic! ${msg}`)->raise + +@get_index external getUnsafe: ('a, string) => option<'b> = "" diff --git a/src/Core__Error.resi b/src/Core__Error.resi index 76c12c0c..a204dd42 100644 --- a/src/Core__Error.resi +++ b/src/Core__Error.resi @@ -169,3 +169,27 @@ Error.panic("Uh oh. This was unexpected!") ``` */ let panic: string => 'a + +/** +`getUnsafe` returns a custom property on an error object, given its name. This is useful for working with custom exceptions thrown from JavaScript. + +**Warning:** The return type is not guaranteed to be what you expect. It can be a string or null or anthing else. Run-time errors can occur if you use it as something that it is not. Consider the functions in the `Type` module to safely access its contents. ReScript exceptions can be handled in a type-safe way. See [Exceptions in ReScript](https://rescript-lang.org/docs/manual/latest/exception). + +## Examples +```rescript +switch exn->Error.fromException { +| None => raise(exn) +| Some(err) => + switch err->Error.getUnsafe("code") { + | None => raise(exn) + | Some(code) => { + if (code === "invalid-password") { + Console.log("Try again!") + } + } + } +} +``` +*/ +@get_index +external getUnsafe: (t, string) => option<'a> = "" diff --git a/test/ErrorTests.mjs b/test/ErrorTests.mjs index a9af7afb..c202c511 100644 --- a/test/ErrorTests.mjs +++ b/test/ErrorTests.mjs @@ -2,6 +2,7 @@ import * as Test from "./Test.mjs"; import * as Js_exn from "rescript/lib/es6/js_exn.js"; +import * as Caml_option from "rescript/lib/es6/caml_option.js"; import * as RescriptCore from "../src/RescriptCore.mjs"; import * as Caml_js_exceptions from "rescript/lib/es6/caml_js_exceptions.js"; @@ -33,7 +34,43 @@ function panicTest(param) { panicTest(undefined); +function catchCustomError(param) { + var authenticationError = new Error("authentication error"); + var codeCaught; + authenticationError["code"] = "invalid-password"; + try { + throw authenticationError; + } + catch (raw_exn){ + var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); + var err = Caml_js_exceptions.as_js_exn(exn); + if (err !== undefined) { + var code = Caml_option.valFromOption(err)["code"]; + if (code !== undefined) { + codeCaught = Caml_option.valFromOption(code); + } + + } else { + throw exn; + } + } + Test.run([ + [ + "ErrorTests.res", + 34, + 15, + 39 + ], + "Can access custom code" + ], codeCaught, (function (prim0, prim1) { + return prim0 === prim1; + }), "invalid-password"); +} + +catchCustomError(undefined); + export { panicTest , + catchCustomError , } /* Not a pure module */ diff --git a/test/ErrorTests.res b/test/ErrorTests.res index 07dcc078..f1b09414 100644 --- a/test/ErrorTests.res +++ b/test/ErrorTests.res @@ -9,3 +9,33 @@ let panicTest = () => { } panicTest() + +// This test case is based on catching authentication errors from the Firebase +// SDK. Errors are subclassed from the Error object and contain custom +// properties like "code". +let catchCustomError = () => { + let authenticationError = Error.make("authentication error") + let codeCaught = ref(None) + Object.set(authenticationError->Obj.magic, "code", "invalid-password") + try { + authenticationError->Error.raise + } catch { + | _ as exn => + switch exn->Error.fromException { + | None => raise(exn) + | Some(err) => + switch err->Error.getUnsafe("code") { + | None => () + | Some(code) => codeCaught := Some(code) + } + } + } + Test.run( + __POS_OF__("Can access custom code"), + codeCaught.contents, + \"==", + Some("invalid-password"), + ) +} + +catchCustomError() diff --git a/test/TestSuite.mjs b/test/TestSuite.mjs index 2085495e..266545e6 100644 --- a/test/TestSuite.mjs +++ b/test/TestSuite.mjs @@ -27,6 +27,8 @@ var Concurrently = PromiseTest.Concurrently; var panicTest = ErrorTests.panicTest; +var catchCustomError = ErrorTests.catchCustomError; + var $$catch = IntTests.$$catch; var eq = ResultTests.eq; @@ -46,6 +48,7 @@ export { Catching , Concurrently , panicTest , + catchCustomError , $$catch , eq , forEachIfOkCallFunction , From 4f83f96080f1baf1f831fe35bcd686be0f2ba044 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Fri, 24 Mar 2023 12:05:53 -0700 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96d70a5b..f99ad419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### API changes - Add `Result.forEach` https://github.com/rescript-association/rescript-core/pull/116 +- Add `Error.getUnsafe` https://github.com/rescript-association/rescript-core/pull/123 ## 0.2.0 From b1b3f4f044033c36287f244db3d9da5c69003513 Mon Sep 17 00:00:00 2001 From: jmagaram Date: Sat, 25 Mar 2023 10:38:50 -0700 Subject: [PATCH 3/3] simpler code example --- src/Core__Error.resi | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Core__Error.resi b/src/Core__Error.resi index a204dd42..a8d69268 100644 --- a/src/Core__Error.resi +++ b/src/Core__Error.resi @@ -181,12 +181,8 @@ switch exn->Error.fromException { | None => raise(exn) | Some(err) => switch err->Error.getUnsafe("code") { - | None => raise(exn) - | Some(code) => { - if (code === "invalid-password") { - Console.log("Try again!") - } - } + | Some("invalid-password") => Console.log("Try again!") + | _ => raise(exn) } } ```