Merge pull request #1223 from napi-rs/strict-check-optional

fix(napi): validate fn for Option<T>
This commit is contained in:
LongYinan 2022-07-06 14:34:30 +08:00 committed by GitHub
commit 1ac7fcf2ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 2 deletions

View file

@ -74,6 +74,8 @@ impl TypeName for JsUnknown {
}
}
impl ValidateNapiValue for JsUnknown {}
impl<T: NapiRaw> ToNapiValue for T {
unsafe fn to_napi_value(_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
Ok(unsafe { NapiRaw::raw(&val) })
@ -158,6 +160,32 @@ impl<T: TypeName> TypeName for Option<T> {
}
}
impl<T: ValidateNapiValue> ValidateNapiValue for Option<T> {
unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
let mut result = -1;
check_status!(
unsafe { sys::napi_typeof(env, napi_val, &mut result) },
"Failed to detect napi value type",
)?;
let received_type = ValueType::from(result);
if received_type == ValueType::Null {
Ok(ptr::null_mut())
} else if let Ok(validate_ret) = unsafe { T::validate(env, napi_val) } {
Ok(validate_ret)
} else {
Err(Error::new(
Status::InvalidArg,
format!(
"Expect value to be Option<{}>, but received {}",
T::value_type(),
received_type
),
))
}
}
}
impl<T> FromNapiValue for Option<T>
where
T: FromNapiValue,

View file

@ -56,7 +56,6 @@ impl ValidateNapiValue for Undefined {}
impl FromNapiValue for Undefined {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
// TODO: with typecheck
match type_of!(env, napi_val) {
Ok(ValueType::Undefined) => Ok(()),
_ => Err(Error::new(
@ -73,7 +72,7 @@ impl ToNapiValue for Undefined {
check_status!(
unsafe { sys::napi_get_undefined(env, &mut ret) },
"Failed to create napi null value"
"Failed to create napi undefined value"
)?;
Ok(ret)

View file

@ -19,6 +19,7 @@ import {
validateUndefined,
returnUndefinedIfInvalid,
returnUndefinedIfInvalidPromise,
validateOptional,
} from '../index'
test('should validate array', (t) => {
@ -180,3 +181,16 @@ test('should return Promise.reject() if arg is not Promise', async (t) => {
// @ts-expect-error
await t.throwsAsync(() => returnUndefinedIfInvalidPromise(1))
})
test('should validate Option<T>', (t) => {
t.is(validateOptional(null, null), false)
t.is(validateOptional(null, false), false)
t.is(validateOptional('1', false), true)
t.is(validateOptional(null, true), true)
// @ts-expect-error
t.throws(() => validateOptional(1, null))
// @ts-expect-error
t.throws(() => validateOptional(null, 2))
// @ts-expect-error
t.throws(() => validateOptional(1, 2))
})

View file

@ -110,6 +110,7 @@ Generated by [AVA](https://avajs.dev).
export function validatePromise(p: Promise<number>): Promise<number>
export function validateString(s: string): string␊
export function validateSymbol(s: symbol): boolean␊
export function validateOptional(input1?: string | undefined | null, input2?: boolean | undefined | null): boolean␊
export function returnUndefinedIfInvalid(input: boolean): boolean␊
export function returnUndefinedIfInvalidPromise(input: Promise<boolean>): Promise<boolean>
export function tsRename(a: { foo: number }): string[]␊

View file

@ -100,6 +100,7 @@ export function validateNumber(i: number): number
export function validatePromise(p: Promise<number>): Promise<number>
export function validateString(s: string): string
export function validateSymbol(s: symbol): boolean
export function validateOptional(input1?: string | undefined | null, input2?: boolean | undefined | null): boolean
export function returnUndefinedIfInvalid(input: boolean): boolean
export function returnUndefinedIfInvalidPromise(input: Promise<boolean>): Promise<boolean>
export function tsRename(a: { foo: number }): string[]

View file

@ -88,6 +88,11 @@ fn validate_symbol(_s: JsSymbol) -> bool {
true
}
#[napi(strict)]
fn validate_optional(input1: Option<String>, input2: Option<bool>) -> bool {
input1.is_some() || input2.unwrap_or(false)
}
#[napi(return_if_invalid)]
fn return_undefined_if_invalid(input: bool) -> bool {
!input