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 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..a8d69268 100644 --- a/src/Core__Error.resi +++ b/src/Core__Error.resi @@ -169,3 +169,23 @@ 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") { + | Some("invalid-password") => Console.log("Try again!") + | _ => raise(exn) + } +} +``` +*/ +@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 ,