From 2e53bf7f9a198aa7ce14c7a60d7cf14f5cfc5f49 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Tue, 5 Jul 2022 18:39:12 +0800 Subject: [PATCH] feat(napi-derive): support return_if_invalid --- crates/backend/src/ast.rs | 1 + crates/backend/src/codegen/fn.rs | 12 +++++++++++- crates/macro/src/parser/attrs.rs | 1 + crates/macro/src/parser/mod.rs | 1 + examples/napi/__test__/strict.spec.ts | 14 ++++++++++++++ examples/napi/__test__/typegen.spec.ts.md | 2 ++ examples/napi/__test__/typegen.spec.ts.snap | Bin 3307 -> 3346 bytes examples/napi/index.d.ts | 2 ++ examples/napi/src/fn_strict.rs | 11 +++++++++++ 9 files changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 570f0451..6e1be828 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -15,6 +15,7 @@ pub struct NapiFn { pub vis: syn::Visibility, pub parent: Option, pub strict: bool, + pub return_if_invalid: bool, pub js_mod: Option, pub ts_generic_types: Option, pub ts_args_type: Option, diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index d4d6a5db..e66efee3 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -188,7 +188,17 @@ impl NapiFn { } } _ => { - let type_check = if self.strict { + let type_check = if self.return_if_invalid { + quote! { + if let Ok(maybe_promise) = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index)) { + if !maybe_promise.is_null() { + return Ok(maybe_promise); + } + } else { + return Ok(std::ptr::null_mut()); + } + } + } else if self.strict { quote! { let maybe_promise = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index))?; if !maybe_promise.is_null() { diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index c317a87a..54598ba1 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -50,6 +50,7 @@ macro_rules! attrgen { (readonly, Readonly(Span)), (skip, Skip(Span)), (strict, Strict(Span)), + (return_if_invalid, ReturnIfInvalid(Span)), (object, Object(Span)), (namespace, Namespace(Span, String, Span)), (iterator, Iterator(Span)), diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 34fa64fb..58f2ccd3 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -670,6 +670,7 @@ fn napi_fn_from_decl( comments: extract_doc_comments(&attrs), attrs, strict: opts.strict().is_some(), + return_if_invalid: opts.return_if_invalid().is_some(), js_mod: opts.namespace().map(|(m, _)| m.to_owned()), ts_generic_types: opts.ts_generic_types().map(|(m, _)| m.to_owned()), ts_args_type: opts.ts_args_type().map(|(m, _)| m.to_owned()), diff --git a/examples/napi/__test__/strict.spec.ts b/examples/napi/__test__/strict.spec.ts index d469ae2a..62b98e86 100644 --- a/examples/napi/__test__/strict.spec.ts +++ b/examples/napi/__test__/strict.spec.ts @@ -17,6 +17,8 @@ import { validateSymbol, validateNull, validateUndefined, + returnUndefinedIfInvalid, + returnUndefinedIfInvalidPromise, } from '../index' test('should validate array', (t) => { @@ -166,3 +168,15 @@ test('should validate undefined', (t) => { message: 'Expect value to be Undefined, but received Number', }) }) + +test('should return undefined if arg is invalid', (t) => { + t.is(returnUndefinedIfInvalid(true), false) + // @ts-expect-error + t.is(returnUndefinedIfInvalid(1), undefined) +}) + +test('should return Promise.reject() if arg is not Promise', async (t) => { + t.is(await returnUndefinedIfInvalidPromise(Promise.resolve(true)), false) + // @ts-expect-error + await t.throwsAsync(() => returnUndefinedIfInvalidPromise(1)) +}) diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 4271220a..5702f86f 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -110,6 +110,8 @@ Generated by [AVA](https://avajs.dev). export function validatePromise(p: Promise): Promise␊ export function validateString(s: string): string␊ export function validateSymbol(s: symbol): boolean␊ + export function returnUndefinedIfInvalid(input: boolean): boolean␊ + export function returnUndefinedIfInvalidPromise(input: Promise): Promise␊ export function tsRename(a: { foo: number }): string[]␊ export function overrideIndividualArgOnFunction(notOverridden: string, f: () => string, notOverridden2: number): string␊ export function overrideIndividualArgOnFunctionWithCbArg(callback: (town: string, name?: string | undefined | null) => string, notOverridden: number): object␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index a5f80b44c22a0d0bc928eb1a8efe7c2f59c1e203..3a445810b61f6f906c7d990151d8718719f337ba 100644 GIT binary patch literal 3346 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 literal 3307 zcmV

5Q0RzV&Pi+smeR|? zXI!XVN@BcPZkAlzs?R|04f?x(in}N5BXovb?sC5*`7S{cEx9wpnc;kr`^#h;3jbXG z{3}yjh3x8UITL9ZkSU8;LKP#vND}5Nq82eD0h^Es4;hycjR8{0pMMDgs_6F{kN|H6AL^M1ee*(Uqjb4!JyM!c+1|kX<-}JKKS&xih>gMhZuyUV_W?T~ZOF{NXD&)X2pvSv=`~CNCM?VgZhoc^e()pMr z7Gjb{zTzSR{i*Ln5=gJpBmE?yi)T_LJet1HOT1!A5-H{^Lzm7XE{UUb?ocqWFKU1? zoohgNFDtN}RtA-LEkT7yU1&|#AW(n%u-SdNh@YSh2Q}Yz@Hkri1BAH zYNgO6Fxq@+hvX-)Z}3eXf$#hTiVnjy*BC0AsJ2xa$eM+3DTrQVi8L$M(W9*|Anq}Q zgDMxdB8$!op^ua2i=y9Xb zU6^YR;QlNTk=WIEhdd0qye%F?*v&&8rHWNB@u#kLoRwP15*<1NBZNi4%WP0rIRo{u z#Dvb{q%2=<&v|BOVcD4Po+f3HbDRp577n&s7KMkg5Fw*cZTXVLX}K@ztF=k|c;$NV z1ay}Pt*uO3Pp&ybyjKu=EsgPs#HeQ_y~9*}X_C7fYXBA*!a2bz9U$G01U{`#zu0#} zySn+UOt);yKE##d*oMFc4)f5eLp?lG2vA_+>6~29Fa-yo2^PhX0>=?hCvI;^;}2v4 zw8^7dxu|j9@F>tGgj9EEa;_^rLUzRz62_x$_T~-vvD7gvdq1EG-f!CXBP!J*!%Y2i z%shn!I?L_NV}Qf;vYPgLk-~c{?8guT&k0BiT(cM=en2J=k-;7#c9{#7N+a$@vp9oP zDuG4k9*%IG!})C-?29yk=g0UwEk%Z|js_Lj$bWoDKXyb$*;b^7tz(xk}`w!!cu|0i>7ta_IavY!B!FKqAmOb-{-I+PSW8%^+2b3?#+TDl6 zH0^^2#l3l2jWL2gG%yQh!Qedv>Zf{UjrDZlZZTIi6gifO*dA8?d9ui!T9Ggzdcmq} z>_U(D{1zC+ZrbF$R4ry#vdBS{Xcc0mMeQgeopbkX6>N0k< zQs3L~j5v9$UIWoq9<|)jijkF|UR-Nf*Jxoak<9(2n$1T;BcEC<=8A#!DZHa)Ivl_FWKC4;f zWfMLY5%qoUMXQ0Kw^0z<-mrzl2FvbY?P#sF-&=CZ1-tkv6l1EDfQOu7LZ*7633Rn8 z^r>B4rA0H*4@0woHeBoasbtzPrc;)0ZbJc-Ro*pPs2;$puhD29t^iJT*MED^A8M}p zwm99vO03|+l?FV91rX=bOcJmIO#qwmB@#@c6C*>Ak-(5wqShrWBUNFwSRKQwuPSQH zwb-gQ7tNE-pjV=}WQzezw-Sa#D}N)2<;q}2qbW1>KU!E8mRN3+i`RLJl`LT4a9yuDq z-Xn%s5B9!+=Vea6Q2@O&0I%_VZI`po2pSrD9tR$b{Y^&Jv`g7L2P{)gVB@r|1sr)HnwCy_#%3Ss(l@g^qM{9r(n` zy(T*n{8-mm2NBx4skfZEt0iL?-^=Q>j)kv*U4mzdRQ1O&I{-Z=A`HR*TGHgIf)c5% zL#j(8pX%05Ll5;}4fx+{# z-o1Od2&UINF3#HVB1lXd-snzUAG!`4ORzUvq^;3E)En0ofm00 zo$5`Vv1~jvZzuy@vn(4$1s`eNj)1nR0hAZYsfu%|`qP-5ke;)xF&>LCJmxmz9W&PT z?{F^8@!SIIG#wo@UiQ%oFO|L-pDqb|@s|4wTX^RT^p!0P<4&)#Dj{hc=wnD-2j*c+ z2jWKdfl9$U3>q0iplec5D%K_J%5-6Kx)`$~#_(Ry7LH~0cFMx+0)H8kdC`^;L~_P( zI`ziRjdW<0yXL;eG0UcXHc=qk#rAFQmH=y3bi|`ibd)A4WNl5!K<87kz>ALv!HLrF ze}&-j?_W!?AYzv%G@hZ54q;<>hg6;qL}+|0UjPpp%0aQbqpxLOO8m~~IWZ3vrJ0x4 z4I2ZLJsvp4X-3K(BsH#MmuXm~Z4vz`**mIcO-^O-Z7=LvfAMupmQ@2MloY*l`zfKD zMO>2RV;DDMXMm6y{*UM<en{n?`QeD!PXs5KB}eU zUX6sw$W4$6@@18Q+9hq>nriW&;g)$d+CCp^pi|S@ZE%HwStMs@o(I|BnQgN$h8D-X zt4VMD4%(T1O?mD~#$4w_+70V%8-dd#(f5vdw2L67wgkTlaJK~8`HkGycJqYAqP7_U zPuy&qK#8T8xuQSO&rNi2J~zkKI5-#%PtuURT0MKAsfyZ(}e*nRZKJ%uFWB|glCv^=v3KsK&xsjr>_M0uW)ZeZ(?P06+)o4G_K>W zqBYPkUlGEBRAgzyY;@O}WUYMW3!}U;#v{V3tC_L*(s6eG?ZK;~!@Z-CWAP0aKifY( zdE9pEV=_4aZEig+kaX&9paek634K65jodB`GxNa;QBRi!+mv$#GcWF{>*?wa-v@jO prKabyaMPl@EmwE0KwEgsyW(!AS*IUXUZ~yN{Xd^BL|}g|004UuTi*Zx diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 5d537f7f..8c4bf1b7 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -100,6 +100,8 @@ export function validateNumber(i: number): number export function validatePromise(p: Promise): Promise export function validateString(s: string): string export function validateSymbol(s: symbol): boolean +export function returnUndefinedIfInvalid(input: boolean): boolean +export function returnUndefinedIfInvalidPromise(input: Promise): Promise export function tsRename(a: { foo: number }): string[] export function overrideIndividualArgOnFunction(notOverridden: string, f: () => string, notOverridden2: number): string export function overrideIndividualArgOnFunctionWithCbArg(callback: (town: string, name?: string | undefined | null) => string, notOverridden: number): object diff --git a/examples/napi/src/fn_strict.rs b/examples/napi/src/fn_strict.rs index 2b2a9cf0..5ce758a4 100644 --- a/examples/napi/src/fn_strict.rs +++ b/examples/napi/src/fn_strict.rs @@ -87,3 +87,14 @@ fn validate_string(s: String) -> String { fn validate_symbol(_s: JsSymbol) -> bool { true } + +#[napi(return_if_invalid)] +fn return_undefined_if_invalid(input: bool) -> bool { + !input +} + +#[napi(return_if_invalid)] +async fn return_undefined_if_invalid_promise(input: Promise) -> Result { + let input_value = input.await?; + Ok(!input_value) +}