diff --git a/moon.mod.json b/moon.mod.json index 41ef90e..4ea3740 100644 --- a/moon.mod.json +++ b/moon.mod.json @@ -1,6 +1,6 @@ { "name": "rami3l/js-ffi", - "version": "0.3.0", + "version": "0.3.1", "readme": "README.md", "repository": "", "license": "Apache-2.0", diff --git a/src/js/async.mbt b/src/js/async.mbt index 44718eb..d8c9870 100644 --- a/src/js/async.mbt +++ b/src/js/async.mbt @@ -1,7 +1,7 @@ ///| pub async fn[T, E : Error] suspend( f : ((T) -> Unit, (E) -> Unit) -> Unit -) -> T!E = "%async.suspend" +) -> T raise E = "%async.suspend" ///| pub fn async_run(f : async () -> Unit) -> Unit = "%async.run" @@ -14,7 +14,8 @@ pub fn async_run(f : async () -> Unit) -> Unit = "%async.run" /// (i.e. this `Promise` is not handled by an external JavaScript API). /// This makes sure that when the operation in the this `Promise` errs out, /// the error is caught by the MoonBit runtime. -pub extern type Promise +#external +pub type Promise ///| extern "js" fn Promise::wait_ffi( @@ -25,8 +26,8 @@ extern "js" fn Promise::wait_ffi( #| (self, on_ok, on_err) => self.then((t) => on_ok(t), (e) => on_err(e)) ///| -pub async fn Promise::wait(self : Promise) -> Value! { - suspend!(fn(k, ke) { Promise::wait_ffi(self, k, fn(e) { ke(Error_(e)) }) }) +pub async fn Promise::wait(self : Promise) -> Value raise { + suspend(fn(k, ke) { Promise::wait_ffi(self, k, fn(e) { ke(Error_(e)) }) }) } ///| @@ -38,18 +39,18 @@ pub async fn Promise::wait(self : Promise) -> Value! { /// This makes sure that when `op` errs out, the error is caught by the MoonBit runtime. /// /// If you don't care about the result of the operation, you can use `spawn_detach` instead. -pub fn[T] Promise::unsafe_new(op : async () -> T!) -> Promise { - Promise::new_ffi(fn() { Value::cast_from(op!()) }) +pub fn[T] Promise::unsafe_new(op : async () -> T raise) -> Promise { + Promise::new_ffi(fn() { Value::cast_from(op()) }) } ///| -extern "js" fn Promise::new_ffi(op : async () -> Value!) -> Promise = +extern "js" fn Promise::new_ffi(op : async () -> Value raise) -> Promise = #| (op) => new Promise((k, ke) => op(k, ke)) ///| -pub fn[T, E : Error] spawn_detach(op : async () -> T!E) -> Unit { +pub fn[T, E : Error] spawn_detach(op : async () -> T raise E) -> Unit { async_run(fn() { - try op!() |> ignore catch { + try op() |> ignore catch { _ => () } }) @@ -62,21 +63,23 @@ pub extern "js" fn Promise::all(promises : Array[Promise]) -> Promise = "(ps) => ///| /// Wraps each given `async fn` in a `Promise` and waits for all of them to resolve. -pub async fn[T] async_all!(ops : Array[async () -> T!]) -> Array[T] { - async_all_raw!(ops.map(fn(op) { async fn!() { Value::cast_from(op!()) } })).map( +pub async fn[T] async_all(ops : Array[async () -> T raise]) -> Array[T] raise { + async_all_raw(ops.map(fn(op) { async fn() raise { Value::cast_from(op()) } })).map( Value::cast, ) } ///| -async fn async_all_raw!(ops : Array[async () -> Value!]) -> Array[Value] { - Promise::all(ops.map(Promise::unsafe_new)).wait!().cast() +async fn async_all_raw( + ops : Array[async () -> Value raise] +) -> Array[Value] raise { + Promise::all(ops.map(Promise::unsafe_new)).wait().cast() } ///| -pub fn async_test(op : async () -> Unit!) -> Unit { +pub fn async_test(op : async () -> Unit raise) -> Unit { async_run(async fn() { - try op!() catch { + op() catch { e => { println("ERROR in `async_test`: \{e}") panic() diff --git a/src/js/async_test.mbt b/src/js/async_test.mbt index 6bcda65..f1de729 100644 --- a/src/js/async_test.mbt +++ b/src/js/async_test.mbt @@ -5,7 +5,7 @@ async fn op1() -> String? { ///| async fn op2() -> String? { - guard op1!() is Some(prefix) + guard op1() is Some(prefix) Some(prefix + ", World") } @@ -16,19 +16,20 @@ async fn op3() -> String? { ///| test "Promise::wait" { - @js.async_test(fn!() { + @js.async_test(fn() raise { // Promise::unsafe_new + Promise::wait is a noop. - let res = @js.Promise::unsafe_new(fn() { op1!() }).wait!() - assert_eq!(res.cast(), Some("Hello")) + let res = @js.Promise::unsafe_new(fn() { op1() }).wait() + assert_eq(res.cast(), Some("Hello")) }) } ///| test "async_all" { - @js.async_test(fn!() { - assert_eq!( - @js.async_all!([fn() { op2!() }, fn() { op1!() }, fn() { op3!() }]), - [Some("Hello, World"), Some("Hello"), None], - ) + @js.async_test(fn() raise { + assert_eq(@js.async_all([fn() { op2() }, fn() { op1() }, fn() { op3() }]), [ + Some("Hello, World"), + Some("Hello"), + None, + ]) }) } diff --git a/src/js/error.mbt b/src/js/error.mbt index 66bf523..d633585 100644 --- a/src/js/error.mbt +++ b/src/js/error.mbt @@ -1,5 +1,5 @@ ///| -pub type! Error_ Value +pub suberror Error_ Value ///| extern "js" fn Error_::cause_ffi(self : Value) -> Value = "(self) => self.cause" @@ -34,7 +34,7 @@ extern "js" fn Error_::wrap_ffi( pub fn[T] Error_::wrap( op : () -> Value, map_ok~ : (Value) -> T = Value::cast -) -> T!Error_ { +) -> T raise Error_ { let mut res : Result[Value, Error_] = Ok(Value::undefined()) Error_::wrap_ffi(op, fn(v) { res = Ok(v) }, fn(e) { res = Err(Error_(e)) }) match res { diff --git a/src/js/js.mbti b/src/js/js.mbti index 618c059..cef0af2 100644 --- a/src/js/js.mbti +++ b/src/js/js.mbti @@ -5,64 +5,38 @@ import( ) // Values -async fn[T] async_all(Array[() -> T!]) -> Array[T]! +async fn[T] async_all(Array[() -> T raise]) -> Array[T] raise let async_iterator : Symbol fn async_run(() -> Unit) -> Unit -fn async_test(() -> Unit!) -> Unit - -fn extends(Value, Value) -> Value - -fn[T] get_with_index(Value, Int) -> T - -fn[T] get_with_string(Value, String) -> T - -fn[T] get_with_symbol(Value, Symbol) -> T +fn async_test(() -> Unit raise) -> Unit let globalThis : Value -fn is_bool(Value) -> Bool - -fn is_null(Value) -> Bool - -fn is_number(Value) -> Bool - -fn is_object(Value) -> Bool - -fn is_string(Value) -> Bool - -fn is_symbol(Value) -> Bool - -fn is_undefined(Value) -> Bool - let iterator : Symbol fn require(String, keys~ : Array[String] = ..) -> Value -fn[T] set_with_index(Value, Int, T) -> Unit - -fn[T] set_with_string(Value, String, T) -> Unit - -fn[T] set_with_symbol(Value, Symbol, T) -> Unit - -fn[T, E : Error] spawn_detach(() -> T!E) -> Unit +fn[T, E : Error] spawn_detach(() -> T raise E) -> Unit -async fn[T, E : Error] suspend(((T) -> Unit, (E) -> Unit) -> Unit) -> T!E +async fn[T, E : Error] suspend(((T) -> Unit, (E) -> Unit) -> Unit) -> T raise E // Types and methods -pub type! Error_ Value +pub suberror Error_ Value fn Error_::cause(Self) -> Value? -fn[T] Error_::wrap(() -> Value, map_ok~ : (Value) -> T = ..) -> T!Self +fn[T] Error_::wrap(() -> Value, map_ok~ : (Value) -> T = ..) -> T raise Self impl Show for Error_ type Nullable[_] fn[T] Nullable::from_option(T?) -> Self[T] +#deprecated fn[T] Nullable::get_exn(Self[T]) -> T fn[T] Nullable::is_null(Self[T]) -> Bool fn[T] Nullable::null() -> Self[T] fn[T] Nullable::to_option(Self[T]) -> T? +fn[T] Nullable::unwrap(Self[T]) -> T pub type Object Value fn[K, V] Object::extend_iter(Self, Iter[(K, V)]) -> Unit @@ -72,6 +46,7 @@ fn[K, V] Object::from_iter(Iter[(K, V)]) -> Self fn[K, V] Object::from_iter2(Iter2[K, V]) -> Self fn Object::from_value(Value) -> Optional[Self] fn Object::from_value_unchecked(Value) -> Self +fn Object::inner(Self) -> Value fn Object::new() -> Self fn[K, V] Object::op_get(Self, K) -> V fn[K, V] Object::op_set(Self, K, V) -> Unit @@ -79,15 +54,18 @@ fn Object::to_value(Self) -> Value type Optional[_] fn[T] Optional::from_option(T?) -> Self[T] +#deprecated fn[T] Optional::get_exn(Self[T]) -> T fn[T] Optional::is_undefined(Self[T]) -> Bool fn[T] Optional::to_option(Self[T]) -> T? fn[T] Optional::undefined() -> Self[T] +fn[T] Optional::unwrap(Self[T]) -> T -pub extern type Promise +#external +pub type Promise fn Promise::all(Array[Self]) -> Self -fn[T] Promise::unsafe_new(() -> T!) -> Self -async fn Promise::wait(Self) -> Value! +fn[T] Promise::unsafe_new(() -> T raise) -> Self +async fn Promise::wait(Self) -> Value raise type Symbol fn Symbol::make() -> Self @@ -179,7 +157,8 @@ fn[A, B, C, D, E, F : Cast, G, H] Union8::to5(Self[A, B, C, D, E, F, G, H]) -> F fn[A, B, C, D, E, F, G : Cast, H] Union8::to6(Self[A, B, C, D, E, F, G, H]) -> G? fn[A, B, C, D, E, F, G, H : Cast] Union8::to7(Self[A, B, C, D, E, F, G, H]) -> H? -pub extern type Value +#external +pub type Value fn[Arg, Result] Value::apply(Self, Array[Arg]) -> Result fn[Arg, Result] Value::apply_with_index(Self, Int, Array[Arg]) -> Result fn[Arg, Result] Value::apply_with_string(Self, String, Array[Arg]) -> Result @@ -187,8 +166,8 @@ fn[Arg, Result] Value::apply_with_symbol(Self, Symbol, Array[Arg]) -> Result fn[T] Value::cast(Self) -> T fn[T] Value::cast_from(T) -> Self fn Value::extends(Self, Self) -> Self -fn Value::from_json(Json) -> Self! -fn Value::from_json_string(String) -> Self! +fn Value::from_json(Json) -> Self raise +fn Value::from_json_string(String) -> Self raise fn[T] Value::get_with_index(Self, Int) -> T fn[T] Value::get_with_string(Self, String) -> T fn[T] Value::get_with_symbol(Self, Symbol) -> T @@ -206,8 +185,8 @@ fn[Arg, Result] Value::new_with_symbol(Self, Symbol, Array[Arg]) -> Result fn[T] Value::set_with_index(Self, Int, T) -> Unit fn[T] Value::set_with_string(Self, String, T) -> Unit fn[T] Value::set_with_symbol(Self, Symbol, T) -> Unit -fn Value::to_json(Self) -> Json! -fn Value::to_json_string(Self) -> String! +fn Value::to_json(Self) -> Json raise +fn Value::to_json_string(Self) -> String raise fn Value::to_string(Self) -> String impl Show for Value impl @json.FromJson for Value diff --git a/src/js/null.mbt b/src/js/null.mbt index e6ae8fa..bb13675 100644 --- a/src/js/null.mbt +++ b/src/js/null.mbt @@ -1,5 +1,6 @@ ///| -extern type Nullable[_] +#external +type Nullable[_] ///| pub fn[T] Nullable::is_null(self : Nullable[T]) -> Bool { @@ -7,8 +8,17 @@ pub fn[T] Nullable::is_null(self : Nullable[T]) -> Bool { } ///| +#deprecated("get_exn does not check for null values. Use unwrap instead") pub fn[T] Nullable::get_exn(self : Nullable[T]) -> T = "%identity" +///| Unwraps the nullable value, panicking if it is null. +pub fn[T] Nullable::unwrap(self : Nullable[T]) -> T { + if self.is_null() { + abort("Cannot unwrap a null value") + } + self.get_exn() +} + ///| pub fn[T] Nullable::to_option(self : Nullable[T]) -> T? { guard not(Value::cast_from(self).is_null()) else { None } diff --git a/src/js/object.mbt b/src/js/object.mbt index 1cc542f..c6e1edc 100644 --- a/src/js/object.mbt +++ b/src/js/object.mbt @@ -11,7 +11,7 @@ pub fn Object::from_value_unchecked(value : Value) -> Object { ///| pub fn Object::to_value(self : Object) -> Value { - self._ + self.inner() } ///| @@ -52,10 +52,10 @@ pub fn[K, V] Object::from_iter2(it : Iter2[K, V]) -> Object { ///| pub fn[K, V] Object::op_get(self : Object, key : K) -> V { - self._.get_ffi(Value::cast_from(key)).cast() + self.inner().get_ffi(Value::cast_from(key)).cast() } ///| pub fn[K, V] Object::op_set(self : Object, key : K, value : V) -> Unit { - self._.set_ffi(Value::cast_from(key), Value::cast_from(value)) + self.inner().set_ffi(Value::cast_from(key), Value::cast_from(value)) } diff --git a/src/js/object_test.mbt b/src/js/object_test.mbt index 94b16bd..7e83c51 100644 --- a/src/js/object_test.mbt +++ b/src/js/object_test.mbt @@ -1,12 +1,12 @@ ///| test "Object::extend_object" { let obj = @js.Object::from_value_unchecked( - @json.from_json!({ "a": 1, "b": 2, "c": 3 }), + @json.from_json({ "a": 1, "b": 2, "c": 3 }), ) let obj1 = @js.Object::from_value_unchecked( - @json.from_json!({ "b": [6, 7], "d": 44, "a": 55 }), + @json.from_json({ "b": [6, 7], "d": 44, "a": 55 }), ) - @json.inspect!(obj.extend_object(obj1)._.to_json!(), content={ + @json.inspect(obj.extend_object(obj1).inner().to_json(), content={ "a": 55, "b": [6, 7], "c": 3, diff --git a/src/js/optional.mbt b/src/js/optional.mbt index 492bddf..a98f3f4 100644 --- a/src/js/optional.mbt +++ b/src/js/optional.mbt @@ -1,14 +1,24 @@ ///| -extern type Optional[_] +#external +type Optional[_] ///| pub fn[T] Optional::is_undefined(self : Optional[T]) -> Bool { self |> Value::cast_from |> Value::is_undefined } -///| +///| +#deprecated("get_exn does not check for undefined values. Use unwrap instead") pub fn[T] Optional::get_exn(self : Optional[T]) -> T = "%identity" +///| Unwraps the optional value, panicking if it is undefined. +pub fn[T] Optional::unwrap(self : Self[T]) -> T { + if self.is_undefined() { + abort("Cannot unwrap an undefined value") + } + self.get_exn() +} + ///| pub fn[T] Optional::to_option(self : Optional[T]) -> T? { if Value::cast_from(self).is_undefined() { diff --git a/src/js/require.mbt b/src/js/require.mbt index fc2e7f1..10f9596 100644 --- a/src/js/require.mbt +++ b/src/js/require.mbt @@ -3,7 +3,7 @@ extern "js" fn require_ffi(path : String) -> Value = "(path) => require(path)" ///| pub fn require(path : String, keys~ : Array[String] = []) -> Value { - keys.fold(init=require_ffi(path), fn { - acc, key => acc.get_ffi(Value::cast_from(key)) - }) + keys.fold(init=require_ffi(path), (acc, key) => acc.get_ffi( + Value::cast_from(key), + )) } diff --git a/src/js/symbol.mbt b/src/js/symbol.mbt index f4fd347..d579238 100644 --- a/src/js/symbol.mbt +++ b/src/js/symbol.mbt @@ -1,5 +1,6 @@ ///| -extern type Symbol +#external +type Symbol ///| pub fn Symbol::make() -> Symbol { diff --git a/src/js/union.mbt b/src/js/union.mbt index 9cfa941..ff46a18 100644 --- a/src/js/union.mbt +++ b/src/js/union.mbt @@ -1,5 +1,6 @@ ///| Union type `A | B` for JavaScript values. -extern type Union2[_, _] +#external +type Union2[_, _] ///| pub fn[A : Cast, B] Union2::to0(self : Union2[A, B]) -> A? { @@ -22,7 +23,8 @@ pub fn[A, B : Cast] Union2::from1(value : B) -> Union2[A, B] { } ///| Union type `A | B | C` for JavaScript values. -extern type Union3[_, _, _] +#external +type Union3[_, _, _] ///| pub fn[A : Cast, B, C] Union3::to0(self : Union3[A, B, C]) -> A? { @@ -55,7 +57,8 @@ pub fn[A, B, C : Cast] Union3::from2(value : C) -> Union3[A, B, C] { } ///| Union type `A | B | C | D` for JavaScript values. -extern type Union4[_, _, _, _] +#external +type Union4[_, _, _, _] ///| pub fn[A : Cast, B, C, D] Union4::to0(self : Union4[A, B, C, D]) -> A? { @@ -98,7 +101,8 @@ pub fn[A, B, C, D : Cast] Union4::from3(value : D) -> Union4[A, B, C, D] { } ///| Union type `A | B | C | D | E` for JavaScript values. -extern type Union5[_, _, _, _, _] +#external +type Union5[_, _, _, _, _] ///| pub fn[A : Cast, B, C, D, E] Union5::to0(self : Union5[A, B, C, D, E]) -> A? { @@ -151,7 +155,8 @@ pub fn[A, B, C, D, E : Cast] Union5::from4(value : E) -> Union5[A, B, C, D, E] { } ///| Union type `A | B | C | D | E | F` for JavaScript values. -extern type Union6[_, _, _, _, _, _] +#external +type Union6[_, _, _, _, _, _] ///| pub fn[A : Cast, B, C, D, E, F] Union6::to0( @@ -238,7 +243,8 @@ pub fn[A, B, C, D, E, F : Cast] Union6::from5( } ///| Union type `A | B | C | D | E | F | G` for JavaScript values. -extern type Union7[_, _, _, _, _, _, _] +#external +type Union7[_, _, _, _, _, _, _] ///| pub fn[A : Cast, B, C, D, E, F, G] Union7::to0( @@ -339,7 +345,8 @@ pub fn[A, B, C, D, E, F, G : Cast] Union7::from6( } ///| Union type `A | B | C | D | E | F | G | H` for JavaScript values. -extern type Union8[_, _, _, _, _, _, _, _] +#external +type Union8[_, _, _, _, _, _, _, _] ///| pub fn[A : Cast, B, C, D, E, F, G, H] Union8::to0( diff --git a/src/js/value.mbt b/src/js/value.mbt index 66c1edf..1b48672 100644 --- a/src/js/value.mbt +++ b/src/js/value.mbt @@ -1,5 +1,6 @@ ///| -pub extern type Value +#external +pub type Value ///| pub fn[T] Value::cast_from(value : T) -> Value = "%identity" @@ -121,8 +122,8 @@ pub fn[Arg, Result] Value::new_with_index( } ///| -pub fn Value::from_json!(json : Json) -> Value { - @json.from_json!(json) +pub fn Value::from_json(json : Json) -> Value raise { + @json.from_json(json) } ///| @@ -136,36 +137,36 @@ pub impl @json.FromJson for Value with from_json(json : Json, path) { Array(xs) => { let acc = Array::new(capacity=xs.length()) for x in xs { - acc.push((@json.from_json!(x, path~) : Value)) + acc.push((@json.from_json(x, path~) : Value)) } Value::cast_from(acc) } Object(kvs) => { let acc = Object::new() for k, v in kvs { - acc._.set_with_string(k, (@json.from_json!(v, path~) : Value)) + acc.inner().set_with_string(k, (@json.from_json(v, path~) : Value)) } - acc._ + acc.inner() } } } ///| -pub fn Value::from_json_string!(str : String) -> Value { - Error_::wrap!(fn() { Value::from_json_string_ffi(str) }) +pub fn Value::from_json_string(str : String) -> Value raise { + Error_::wrap(fn() { Value::from_json_string_ffi(str) }) } ///| extern "js" fn Value::from_json_string_ffi(str : String) -> Value = "JSON.parse" ///| -pub fn Value::to_json_string!(self : Value) -> String { - Error_::wrap!(fn() { self.to_json_string_ffi() }) +pub fn Value::to_json_string(self : Value) -> String raise { + Error_::wrap(fn() { self.to_json_string_ffi() }) } ///| -pub fn Value::to_json!(self : Value) -> Json { - @json.parse!(self.to_json_string!()) +pub fn Value::to_json(self : Value) -> Json raise { + @json.parse(self.to_json_string()) } ///| diff --git a/src/js/value_test.mbt b/src/js/value_test.mbt index b047c43..533c7e5 100644 --- a/src/js/value_test.mbt +++ b/src/js/value_test.mbt @@ -60,12 +60,12 @@ let json_str_pretty = #|} ///| -let json_val : @js.Value = @js.Value::from_json_string?(json_str_pretty).unwrap() +let json_val : @js.Value = (try? @js.Value::from_json_string(json_str_pretty)).unwrap() ///| test "Value::to_json_string" { - inspect!( - json_val.to_json_string!(), + inspect( + json_val.to_json_string(), content= #|{"_id":"67a6c712bc218526d5e1b516","index":0,"guid":"74e69a18-680a-4a74-a401-e386c30d0a35","isActive":false,"balance":"$3,140.03","picture":"http://placehold.it/32x32","age":33,"eyeColor":"blue","name":"Burt Dominguez","gender":"male","company":"GEEKWAGON","email":"burtdominguez@geekwagon.com","phone":"+1 (913) 583-3488","address":"379 Essex Street, Fresno, Minnesota, 5304","about":"Proident velit ullamco cillum occaecat irure eu proident commodo consectetur est ut elit consectetur.","registered":"2020-12-06T01:54:20 -08:00","latitude":71.680916,"longitude":-88.552886,"tags":["aute","occaecat","qui","esse","commodo","sint","proident"],"friends":[{"id":0,"name":"Gilliam Mcgowan"},{"id":1,"name":"Bryan Stanton"},{"id":2,"name":"Jenifer Elliott"}],"greeting":"Hello, Burt Dominguez! You have 4 unread messages.","favoriteFruit":"apple"} , @@ -74,13 +74,13 @@ test "Value::to_json_string" { ///| test "Value::to_json" { - assert_eq!(json_val.to_json!(), json) + assert_eq(json_val.to_json(), json) } ///| test "Value::from_json" { - assert_eq!( - @js.Value::from_json!(json).to_json_string!(), - json_val.to_json_string!(), + assert_eq( + @js.Value::from_json(json).to_json_string(), + json_val.to_json_string(), ) }