redevplugin_worker_sdk/
resource.rs1use crate::error::{Error, Result};
2use serde::Deserialize;
3
4pub const IO_FLAG_EOF: u32 = 1 << 0;
5pub const IO_FLAG_TEXT: u32 = 1 << 1;
6pub const IO_FLAG_BINARY: u32 = 1 << 2;
7pub const IO_FLAG_MESSAGE_END: u32 = 1 << 3;
8pub const IO_FLAG_DATAGRAM_END: u32 = 1 << 4;
9pub const MAX_IO_CHUNK_BYTES: usize = 64 * 1024;
10
11#[link(wasm_import_module = "redevplugin.io")]
12unsafe extern "C" {
13 #[link_name = "rdp_call_v1"]
14 fn rdp_call_v1(
15 request_ptr: i32,
16 request_len: i32,
17 response_ptr: i32,
18 response_capacity: i32,
19 ) -> i32;
20 #[link_name = "rdp_read_v1"]
21 fn rdp_read_v1(
22 handle: i64,
23 destination_ptr: i32,
24 destination_capacity: i32,
25 flags_ptr: i32,
26 ) -> i32;
27 #[link_name = "rdp_write_v1"]
28 fn rdp_write_v1(handle: i64, source_ptr: i32, source_len: i32, flags: i32) -> i32;
29 #[link_name = "rdp_seek_v1"]
30 fn rdp_seek_v1(handle: i64, offset: i64, whence: i32) -> i64;
31 #[link_name = "rdp_close_v1"]
32 fn rdp_close_v1(handle: i64) -> i32;
33 #[link_name = "rdp_last_error_v1"]
34 fn rdp_last_error_v1(response_ptr: i32, response_capacity: i32) -> i32;
35}
36
37#[derive(Debug)]
38pub(crate) struct Handle {
39 value: Option<u64>,
40}
41
42impl Handle {
43 pub(crate) fn new(value: u64) -> Result<Self> {
44 if value == 0 || value > i64::MAX as u64 {
45 return Err(Error::internal("Host returned an invalid resource handle"));
46 }
47 Ok(Self { value: Some(value) })
48 }
49
50 pub fn id(&self) -> u64 {
51 self.value.unwrap_or(0)
52 }
53
54 pub(crate) fn disarm(&mut self) {
55 self.value = None;
56 }
57
58 pub(crate) fn read(&mut self, capacity: usize) -> Result<(Vec<u8>, u32)> {
59 if capacity == 0 || capacity > MAX_IO_CHUNK_BYTES {
60 return Err(Error::from_abi_status(-1));
61 }
62 let handle = self.require_open()?;
63 let mut bytes = vec![0_u8; capacity];
64 let mut flags = 0_u32;
65 let status = unsafe {
66 rdp_read_v1(
67 handle as i64,
68 bytes.as_mut_ptr() as i32,
69 bytes.len() as i32,
70 (&mut flags as *mut u32) as i32,
71 )
72 };
73 let written = status_result(status)?;
74 if written > bytes.len() {
75 return Err(Error::internal("Host returned an oversized read result"));
76 }
77 bytes.truncate(written);
78 Ok((bytes, flags))
79 }
80
81 pub(crate) fn write(&mut self, source: &[u8], flags: u32) -> Result<()> {
82 if source.len() > MAX_IO_CHUNK_BYTES {
83 return Err(Error::from_abi_status(-11));
84 }
85 let handle = self.require_open()?;
86 let status = unsafe {
87 rdp_write_v1(
88 handle as i64,
89 source.as_ptr() as i32,
90 source.len() as i32,
91 flags as i32,
92 )
93 };
94 let written = status_result(status)?;
95 if written != source.len() {
96 return Err(Error::internal("Host returned a forbidden partial write"));
97 }
98 Ok(())
99 }
100
101 pub(crate) fn seek(&mut self, offset: i64, whence: u32) -> Result<u64> {
102 let handle = self.require_open()?;
103 let status = unsafe { rdp_seek_v1(handle as i64, offset, whence as i32) };
104 if status < 0 {
105 return Err(last_error(status as i32));
106 }
107 Ok(status as u64)
108 }
109
110 pub(crate) fn close(&mut self) -> Result<()> {
111 let Some(handle) = self.value.take() else {
112 return Ok(());
113 };
114 let status = unsafe { rdp_close_v1(handle as i64) };
115 status_result(status).map(|_| ())
116 }
117
118 fn require_open(&self) -> Result<u64> {
119 self.value.ok_or_else(|| Error::from_abi_status(-5))
120 }
121}
122
123impl Drop for Handle {
124 fn drop(&mut self) {
125 let _ = self.close();
126 }
127}
128
129pub(crate) fn call_control_raw(request: &[u8]) -> Result<Vec<u8>> {
130 if request.is_empty() || request.len() > MAX_IO_CHUNK_BYTES {
131 return Err(Error::from_abi_status(-1));
132 }
133 let mut response = vec![0_u8; MAX_IO_CHUNK_BYTES];
134 let status = unsafe {
135 rdp_call_v1(
136 request.as_ptr() as i32,
137 request.len() as i32,
138 response.as_mut_ptr() as i32,
139 response.len() as i32,
140 )
141 };
142 let written = status_result(status)?;
143 if written > response.len() {
144 return Err(Error::internal(
145 "Host returned an oversized control response",
146 ));
147 }
148 response.truncate(written);
149 Ok(response)
150}
151
152fn status_result(status: i32) -> Result<usize> {
153 if status < 0 {
154 return Err(last_error(status));
155 }
156 usize::try_from(status).map_err(|_| Error::internal("ABI result does not fit usize"))
157}
158
159fn last_error(status: i32) -> Error {
160 let mut response = vec![0_u8; MAX_IO_CHUNK_BYTES];
161 let written = unsafe { rdp_last_error_v1(response.as_mut_ptr() as i32, response.len() as i32) };
162 if written > 0 && (written as usize) <= response.len() {
163 response.truncate(written as usize);
164 #[derive(Deserialize)]
165 struct LastError {
166 code: crate::error::ErrorCode,
167 message: String,
168 #[serde(default)]
169 retryable: bool,
170 #[serde(default)]
171 details: serde_json::Value,
172 }
173 if let Ok(error) = serde_json::from_slice::<LastError>(&response) {
174 return Error {
175 code: error.code,
176 message: error.message,
177 retryable: error.retryable,
178 details: error.details,
179 };
180 }
181 }
182 Error::from_abi_status(status)
183}