Skip to main content

ferrijs_std/utils/
option.rs

1use rquickjs::{
2    class::{Trace, Tracer},
3    function::{FromParam, ParamRequirement, ParamsAccessor},
4    Ctx, FromJs, IntoJs, JsLifetime, Result, Type, Value,
5};
6
7/// Helper type for treating an undefined value as None, without treating null as None
8#[derive(Clone)]
9pub struct Undefined<T>(pub Option<T>);
10
11impl<'js, T: FromJs<'js>> FromJs<'js> for Undefined<T> {
12    fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
13        if value.type_of() == Type::Undefined {
14            Ok(Self(None))
15        } else {
16            Ok(Self(Some(FromJs::from_js(ctx, value)?)))
17        }
18    }
19}
20
21impl<'js, T: IntoJs<'js>> IntoJs<'js> for Undefined<T> {
22    fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
23        match self.0 {
24            None => Ok(Value::new_undefined(ctx.clone())),
25            Some(val) => val.into_js(ctx),
26        }
27    }
28}
29
30impl<T> Default for Undefined<T> {
31    fn default() -> Self {
32        Self(None)
33    }
34}
35
36unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Undefined<T> {
37    type Changed<'to> = Undefined<T::Changed<'to>>;
38}
39
40impl<'js, T: Trace<'js>> Trace<'js> for Undefined<T> {
41    fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
42        self.0.trace(tracer)
43    }
44}
45
46/// Helper type for converting an None into null instead of undefined.
47/// Needed while rquickjs::function::Null has no IntoJs implementation
48#[derive(Clone)]
49pub struct Null<T>(pub Option<T>);
50
51impl<'js, T: FromJs<'js>> FromJs<'js> for Null<T> {
52    fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
53        if value.type_of() == Type::Null {
54            Ok(Self(None))
55        } else {
56            Ok(Self(Some(FromJs::from_js(ctx, value)?)))
57        }
58    }
59}
60
61impl<'js, T: IntoJs<'js>> IntoJs<'js> for Null<T> {
62    fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
63        match self.0 {
64            None => Ok(Value::new_null(ctx.clone())),
65            Some(val) => val.into_js(ctx),
66        }
67    }
68}
69
70unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Null<T> {
71    type Changed<'to> = Null<T::Changed<'to>>;
72}
73
74impl<'js, T: Trace<'js>> Trace<'js> for Null<T> {
75    fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
76        self.0.trace(tracer)
77    }
78}
79
80/// Helper type for accepting no value, or null, but considering undefined as a value
81pub struct NullableOpt<T>(pub Option<T>);
82
83impl<'js, T: FromJs<'js>> FromParam<'js> for NullableOpt<T> {
84    fn param_requirement() -> ParamRequirement {
85        ParamRequirement::optional()
86    }
87
88    fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
89        if !params.is_empty() {
90            let arg = params.arg();
91            if arg.is_null() {
92                Ok(NullableOpt(None))
93            } else {
94                let ctx = params.ctx().clone();
95                Ok(NullableOpt(Some(T::from_js(&ctx, arg)?)))
96            }
97        } else {
98            Ok(NullableOpt(None))
99        }
100    }
101}