rquickjs_core/value/
proxy.rs1use crate::{qjs, Ctx, IntoJs, Object, Result};
4
5mod handler;
6pub use handler::{ProxyHandler, ProxyProperty, ProxyReceiver, ProxyTarget};
7
8#[derive(Debug, PartialEq, Clone, Hash, Eq)]
10#[repr(transparent)]
11pub struct Proxy<'js>(pub(crate) Object<'js>);
12
13impl<'js> Proxy<'js> {
14 pub fn new(
16 ctx: Ctx<'js>,
17 target: impl IntoJs<'js>,
18 handler: ProxyHandler<'js>,
19 ) -> Result<Self> {
20 let proxy = unsafe {
21 let target = target.into_js(&ctx)?;
22 let handler = handler.0;
23 let value = qjs::JS_NewProxy(ctx.as_ptr(), target.as_js_value(), handler.as_js_value());
24 let value = ctx.handle_exception(value)?;
25 Object::from_js_value(ctx, value)
26 };
27 Ok(Self(proxy))
28 }
29
30 pub fn target(&self) -> Result<Object<'js>> {
32 unsafe {
33 let target = qjs::JS_GetProxyTarget(self.0.ctx.as_ptr(), self.0.as_js_value());
34 let target = self.0.ctx.handle_exception(target)?;
35 Ok(Object::from_js_value(self.0.ctx.clone(), target))
36 }
37 }
38
39 pub fn handler(&self) -> Result<Object<'js>> {
41 unsafe {
42 let handler = qjs::JS_GetProxyHandler(self.0.ctx.as_ptr(), self.0.as_js_value());
43 let handler = self.0.ctx.handle_exception(handler)?;
44 Ok(Object::from_js_value(self.0.ctx.clone(), handler))
45 }
46 }
47}
48
49#[cfg(test)]
50mod test {
51 use crate::{
52 class::{JsClass, Readable, Trace, Tracer},
53 test_with,
54 value::Constructor,
55 Class, Error, Function, JsLifetime, Value,
56 };
57
58 use super::*;
59
60 #[test]
61 fn from_javascript() {
62 test_with(|ctx| {
63 let proxy: Proxy = ctx
64 .eval(r#"new Proxy({ a: 1 }, { get: () => 2 })"#)
65 .unwrap();
66 let target = proxy.target().unwrap();
67 let handler = proxy.handler().unwrap();
68 let a: i32 = target.get("a").unwrap();
69 assert_eq!(a, 1);
70 let _: Function = handler.get("get").unwrap();
71 });
72 }
73
74 #[test]
75 fn from_rust() {
76 test_with(|ctx| {
77 let handler = ProxyHandler::new(ctx.clone())
78 .unwrap()
79 .with_getter(|target, property, _receiver| {
80 if property.to_string().unwrap() == "a" {
81 let value: Value<'_> = target.0.get("a")?;
82 Ok(value)
83 } else {
84 Err(Error::Unknown)
85 }
86 })
87 .unwrap();
88 let target = Object::new(ctx.clone()).unwrap();
89 target.set("a", 1).unwrap();
90 let proxy = Proxy::new(ctx.clone(), target, handler).unwrap();
91 ctx.globals().set("proxy", proxy).unwrap();
92 let a: i32 = ctx.eval("proxy.a").unwrap();
93 assert_eq!(a, 1);
94 });
95 }
96
97 #[test]
98 fn class_proxy() {
99 pub struct MyClass {
100 a: i32,
101 }
102
103 impl MyClass {
104 pub fn new(a: i32) -> Self {
105 Self { a }
106 }
107 }
108
109 impl<'js> Trace<'js> for MyClass {
110 fn trace<'a>(&self, _tracer: Tracer<'a, 'js>) {}
111 }
112
113 unsafe impl<'js> JsLifetime<'js> for MyClass {
114 type Changed<'to> = MyClass;
115 }
116
117 impl<'js> JsClass<'js> for MyClass {
118 const NAME: &'static str = "MyClass";
119
120 type Mutable = Readable;
121
122 fn constructor(_ctx: &Ctx<'js>) -> Result<Option<Constructor<'js>>> {
123 Ok(None)
124 }
125 }
126
127 test_with(|ctx| {
128 let handler = ProxyHandler::new(ctx.clone())
129 .unwrap()
130 .with_getter(|target, property, _receiver| {
131 if property.to_string().unwrap() == "a" {
132 let target = target.0.into_class::<MyClass>().unwrap();
133 let value = target.borrow().a;
134 Ok(value)
135 } else {
136 Err(Error::Unknown)
137 }
138 })
139 .unwrap();
140 let target = Class::instance(ctx.clone(), MyClass::new(1)).unwrap();
141 let proxy = Proxy::new(ctx.clone(), target, handler).unwrap();
142 ctx.globals().set("proxy", proxy).unwrap();
143 let a: i32 = ctx.eval("proxy.a").unwrap();
144 assert_eq!(a, 1);
145 });
146 }
147}