Skip to content

Commit dae093f

Browse files
authored
Merge pull request #829 from neon-bindings/arguments-options-api
Ergonomic function-calling API
2 parents afca75e + b7617ed commit dae093f

File tree

17 files changed

+610
-67
lines changed

17 files changed

+610
-67
lines changed

src/context/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,10 @@
9494
//!
9595
//! while !done {
9696
//! done = cx.execute_scoped(|mut cx| { // temporary scope
97-
//! let args: Vec<Handle<JsValue>> = vec![];
98-
//! let obj = next.call(&mut cx, iterator, args)? // temporary object
99-
//! .downcast_or_throw::<JsObject, _>(&mut cx)?;
97+
//! let obj: Handle<JsObject> = next // temporary object
98+
//! .call_with(&cx)
99+
//! .this(iterator)
100+
//! .apply(&mut cx)?;
100101
//! let number = obj.get(&mut cx, "value")? // temporary number
101102
//! .downcast_or_throw::<JsNumber, _>(&mut cx)?
102103
//! .value(&mut cx);

src/object/class/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::context::{Context, Lock};
1111
use crate::handle::{Handle, Managed};
1212
use crate::object::{Object, This};
1313
use crate::result::{JsResult, NeonResult, Throw};
14-
use crate::types::internal::{Callback, ValueInternal};
14+
use crate::types::private::{Callback, ValueInternal};
1515
use crate::types::{build, JsFunction, JsValue, Value};
1616
use neon_runtime;
1717
use neon_runtime::raw;

src/types/binary.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::context::internal::Env;
44
use crate::context::{Context, Lock};
55
use crate::handle::Managed;
66
use crate::result::JsResult;
7-
use crate::types::internal::ValueInternal;
7+
use crate::types::private::ValueInternal;
88
use crate::types::{build, Object, Value};
99
use neon_runtime;
1010
use neon_runtime::raw;

src/types/boxed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::context::internal::Env;
88
use crate::context::{Context, FinalizeContext};
99
use crate::handle::{Handle, Managed};
1010
use crate::object::Object;
11-
use crate::types::internal::ValueInternal;
11+
use crate::types::private::ValueInternal;
1212
use crate::types::Value;
1313

1414
type BoxAny = Box<dyn Any + Send + 'static>;

src/types/buffer/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use neon_runtime::{raw, TypedArrayType};
66
use crate::context::{internal::Env, Context};
77
use crate::handle::{Handle, Managed};
88
use crate::result::{JsResult, Throw};
9-
use crate::types::{internal::ValueInternal, Object, Value};
9+
use crate::types::{private::ValueInternal, Object, Value};
1010

1111
use super::lock::{Ledger, Lock};
1212
use super::{private, BorrowError, Ref, RefMut, TypedArray};

src/types/date.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{Value, ValueInternal};
1+
use super::{private::ValueInternal, Value};
22
use crate::context::internal::Env;
33
use crate::context::Context;
44
use crate::handle::{Handle, Managed};

src/types/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use neon_runtime::raw;
88
use crate::context::internal::Env;
99
use crate::context::Context;
1010
use crate::result::{NeonResult, Throw};
11-
use crate::types::internal::ValueInternal;
11+
use crate::types::private::ValueInternal;
1212
use crate::types::utf8::Utf8;
1313
use crate::types::{build, Handle, Managed, Object, Value};
1414

src/types/function/mod.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
//! Types and traits for working with JavaScript functions.
2+
3+
use crate::context::Context;
4+
use crate::handle::Handle;
5+
use crate::object::Object;
6+
use crate::result::{JsResult, NeonResult};
7+
use crate::types::{JsFunction, JsObject, JsValue, Value};
8+
9+
use smallvec::smallvec;
10+
11+
pub(crate) mod private;
12+
13+
/// A builder for making a JavaScript function call like `parseInt("42")`.
14+
///
15+
/// The builder methods make it convenient to assemble the call from parts:
16+
/// ```
17+
/// # use neon::prelude::*;
18+
/// # fn foo(mut cx: FunctionContext) -> JsResult<JsNumber> {
19+
/// # let global = cx.global();
20+
/// # let parse_int = global.get(&mut cx, "parseInt")?;
21+
/// # let parse_int: Handle<JsFunction> = parse_int.downcast_or_throw(&mut cx)?;
22+
/// let x: Handle<JsNumber> = parse_int
23+
/// .call_with(&cx)
24+
/// .arg(cx.string("42"))
25+
/// .apply(&mut cx)?;
26+
/// # Ok(x)
27+
/// # }
28+
/// ```
29+
#[derive(Clone)]
30+
pub struct CallOptions<'a> {
31+
pub(crate) callee: Handle<'a, JsFunction>,
32+
pub(crate) this: Option<Handle<'a, JsValue>>,
33+
pub(crate) args: private::ArgsVec<'a>,
34+
}
35+
36+
impl<'a> CallOptions<'a> {
37+
/// Set the value of `this` for the function call.
38+
pub fn this<V: Value>(&mut self, this: Handle<'a, V>) -> &mut Self {
39+
self.this = Some(this.upcast());
40+
self
41+
}
42+
43+
/// Add an argument to the arguments list.
44+
pub fn arg<V: Value>(&mut self, arg: Handle<'a, V>) -> &mut Self {
45+
self.args.push(arg.upcast());
46+
self
47+
}
48+
49+
/// Replaces the arguments list with the given arguments.
50+
pub fn args<A: Arguments<'a>>(&mut self, args: A) -> &mut Self {
51+
self.args = args.into_args_vec();
52+
self
53+
}
54+
55+
/// Make the function call. If the function returns without throwing, the result value
56+
/// is downcast to the type `V`, throwing a `TypeError` if the downcast fails.
57+
pub fn apply<'b: 'a, V: Value, C: Context<'b>>(&self, cx: &mut C) -> JsResult<'b, V> {
58+
let this = self.this.unwrap_or_else(|| cx.undefined().upcast());
59+
let v: Handle<JsValue> = self.callee.call(cx, this, &self.args)?;
60+
v.downcast_or_throw(cx)
61+
}
62+
63+
/// Make the function call for side effect, discarding the result value. This method is
64+
/// preferable to [`apply()`](CallOptions::apply) when the result value isn't needed,
65+
/// since it doesn't require specifying a result type.
66+
pub fn exec<'b: 'a, C: Context<'b>>(&self, cx: &mut C) -> NeonResult<()> {
67+
let this = self.this.unwrap_or_else(|| cx.undefined().upcast());
68+
self.callee.call(cx, this, &self.args)?;
69+
Ok(())
70+
}
71+
}
72+
73+
/// A builder for making a JavaScript constructor call like `new Array(16)`.
74+
///
75+
/// The builder methods make it convenient to assemble the call from parts:
76+
/// ```
77+
/// # use neon::prelude::*;
78+
/// # fn foo(mut cx: FunctionContext) -> JsResult<JsObject> {
79+
/// # let global = cx.global();
80+
/// # let url = global.get(&mut cx, "URL")?;
81+
/// # let url: Handle<JsFunction> = url.downcast_or_throw(&mut cx)?;
82+
/// let obj = url
83+
/// .construct_with(&cx)
84+
/// .arg(cx.string("https://neon-bindings.com"))
85+
/// .apply(&mut cx)?;
86+
/// # Ok(obj)
87+
/// # }
88+
/// ```
89+
#[derive(Clone)]
90+
pub struct ConstructOptions<'a> {
91+
pub(crate) callee: Handle<'a, JsFunction>,
92+
pub(crate) args: private::ArgsVec<'a>,
93+
}
94+
95+
impl<'a> ConstructOptions<'a> {
96+
/// Add an argument to the arguments list.
97+
pub fn arg<V: Value>(&mut self, arg: Handle<'a, V>) -> &mut Self {
98+
self.args.push(arg.upcast());
99+
self
100+
}
101+
102+
/// Replaces the arguments list with the given arguments.
103+
pub fn args<A: Arguments<'a>>(&mut self, args: A) -> &mut Self {
104+
self.args = args.into_args_vec();
105+
self
106+
}
107+
108+
/// Make the constructor call. If the function returns without throwing, returns
109+
/// the resulting object.
110+
pub fn apply<'b: 'a, O: Object, C: Context<'b>>(&self, cx: &mut C) -> JsResult<'b, O> {
111+
let v: Handle<JsObject> = self.callee.construct(cx, &self.args)?;
112+
v.downcast_or_throw(cx)
113+
}
114+
}
115+
116+
/// The trait for specifying arguments for a function call. This trait is sealed and cannot
117+
/// be implemented by types outside of the Neon crate.
118+
///
119+
/// **Note:** This trait is implemented for tuples of up to 32 JavaScript values,
120+
/// but for the sake of brevity, only tuples up to size 8 are shown in this documentation.
121+
pub trait Arguments<'a>: private::ArgumentsInternal<'a> {}
122+
123+
impl<'a> private::ArgumentsInternal<'a> for () {
124+
fn into_args_vec(self) -> private::ArgsVec<'a> {
125+
smallvec![]
126+
}
127+
}
128+
129+
impl<'a> Arguments<'a> for () {}
130+
131+
macro_rules! impl_arguments {
132+
{
133+
[ $(($tprefix:ident, $vprefix:ident), )* ];
134+
[];
135+
} => {};
136+
137+
{
138+
[ $(($tprefix:ident, $vprefix:ident), )* ];
139+
[ $(#[$attr1:meta])? ($tname1:ident, $vname1:ident), $($(#[$attrs:meta])? ($tnames:ident, $vnames:ident), )* ];
140+
} => {
141+
$(#[$attr1])?
142+
impl<'a, $($tprefix: Value, )* $tname1: Value> private::ArgumentsInternal<'a> for ($(Handle<'a, $tprefix>, )* Handle<'a, $tname1>, ) {
143+
fn into_args_vec(self) -> private::ArgsVec<'a> {
144+
let ($($vprefix, )* $vname1, ) = self;
145+
smallvec![$($vprefix.upcast(),)* $vname1.upcast()]
146+
}
147+
}
148+
149+
$(#[$attr1])?
150+
impl<'a, $($tprefix: Value, )* $tname1: Value> Arguments<'a> for ($(Handle<'a, $tprefix>, )* Handle<'a, $tname1>, ) {}
151+
152+
impl_arguments! {
153+
[ $(($tprefix, $vprefix), )* ($tname1, $vname1), ];
154+
[ $($(#[$attrs])? ($tnames, $vnames), )* ];
155+
}
156+
};
157+
}
158+
159+
impl_arguments! {
160+
[];
161+
[
162+
(V1, v1),
163+
(V2, v2),
164+
(V3, v3),
165+
(V4, v4),
166+
(V5, v5),
167+
(V6, v6),
168+
(V7, v7),
169+
(V8, v8),
170+
#[doc(hidden)]
171+
(V9, v9),
172+
#[doc(hidden)]
173+
(V10, v10),
174+
#[doc(hidden)]
175+
(V11, v11),
176+
#[doc(hidden)]
177+
(V12, v12),
178+
#[doc(hidden)]
179+
(V13, v13),
180+
#[doc(hidden)]
181+
(V14, v14),
182+
#[doc(hidden)]
183+
(V15, v15),
184+
#[doc(hidden)]
185+
(V16, v16),
186+
#[doc(hidden)]
187+
(V17, v17),
188+
#[doc(hidden)]
189+
(V18, v18),
190+
#[doc(hidden)]
191+
(V19, v19),
192+
#[doc(hidden)]
193+
(V20, v20),
194+
#[doc(hidden)]
195+
(V21, v21),
196+
#[doc(hidden)]
197+
(V22, v22),
198+
#[doc(hidden)]
199+
(V23, v23),
200+
#[doc(hidden)]
201+
(V24, v24),
202+
#[doc(hidden)]
203+
(V25, v25),
204+
#[doc(hidden)]
205+
(V26, v26),
206+
#[doc(hidden)]
207+
(V27, v27),
208+
#[doc(hidden)]
209+
(V28, v28),
210+
#[doc(hidden)]
211+
(V29, v29),
212+
#[doc(hidden)]
213+
(V30, v30),
214+
#[doc(hidden)]
215+
(V31, v31),
216+
#[doc(hidden)]
217+
(V32, v32),
218+
];
219+
}

src/types/function/private.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use crate::handle::Handle;
2+
use crate::types::JsValue;
3+
4+
use smallvec::SmallVec;
5+
6+
pub type ArgsVec<'a> = SmallVec<[Handle<'a, JsValue>; 8]>;
7+
8+
/// This type marks the `Arguments` trait as sealed.
9+
pub trait ArgumentsInternal<'a> {
10+
fn into_args_vec(self) -> ArgsVec<'a>;
11+
}

0 commit comments

Comments
 (0)