sqlx_cloudflare_core/
js.rs1use js_sys::{Array, ArrayBuffer, Uint8Array};
8use sqlx_core::error::Error;
9use wasm_bindgen::prelude::*;
10use wasm_bindgen::JsCast;
11
12use crate::{safe_integer, DatabaseError, Value};
13
14pub fn to_js_array(values: &[Value]) -> Result<Array, Error> {
20 values.iter().map(to_js).collect()
21}
22
23pub fn to_js(value: &Value) -> Result<JsValue, Error> {
29 Ok(match value {
30 Value::Null => JsValue::NULL,
31 Value::Integer(value) => {
34 #[allow(
35 clippy::cast_precision_loss,
36 reason = "`safe_integer` has just checked the conversion is exact"
37 )]
38 let value = safe_integer(*value).map_err(Error::Encode)? as f64;
39 JsValue::from_f64(value)
40 }
41 Value::Real(value) => JsValue::from_f64(*value),
42 Value::Text(value) => JsValue::from_str(value),
43 Value::Blob(value) => Uint8Array::from(value.as_slice()).buffer().into(),
46 })
47}
48
49pub fn from_js(value: &JsValue) -> Result<Value, Error> {
55 Ok(if value.is_null() || value.is_undefined() {
56 Value::Null
57 } else if let Some(number) = value.as_f64() {
58 Value::from_number(number)
59 } else if let Some(text) = value.as_string() {
60 Value::Text(text)
61 } else if let Some(boolean) = value.as_bool() {
62 Value::Integer(i64::from(boolean))
63 } else if let Some(buffer) = value.dyn_ref::<ArrayBuffer>() {
64 Value::Blob(Uint8Array::new(buffer).to_vec())
65 } else if let Some(bytes) = value.dyn_ref::<Uint8Array>() {
66 Value::Blob(bytes.to_vec())
67 } else if let Some(numbers) = value.dyn_ref::<Array>() {
68 Value::Blob(bytes_from_numbers(numbers)?)
70 } else {
71 return Err(Error::Decode(
72 format!("a value of an unexpected JavaScript type came back: {value:?}").into(),
73 ));
74 })
75}
76
77fn bytes_from_numbers(numbers: &Array) -> Result<Vec<u8>, Error> {
78 numbers
79 .iter()
80 .map(|number| {
81 number
82 .as_f64()
83 .filter(|number| number.fract() == 0.0 && (0.0..=255.0).contains(number))
84 .map(|number| {
85 #[allow(
86 clippy::cast_possible_truncation,
87 clippy::cast_sign_loss,
88 reason = "the filter has just checked it is a whole number in 0..=255"
89 )]
90 let byte = number as u8;
91 byte
92 })
93 .ok_or_else(|| Error::Decode("a BLOB element came back that is not a byte".into()))
94 })
95 .collect()
96}
97
98#[must_use]
104pub fn database_error(error: &JsValue) -> Error {
105 let message = match error.dyn_ref::<js_sys::Error>() {
106 Some(error) => {
107 let message = String::from(error.message());
108
109 match error.cause().dyn_ref::<js_sys::Error>() {
110 Some(cause) => {
111 let cause = String::from(cause.message());
112 if message.contains(&cause) {
113 message
114 } else {
115 format!("{message}: {cause}")
116 }
117 }
118 None => message,
119 }
120 }
121 None => error.as_string().unwrap_or_else(|| format!("{error:?}")),
122 };
123
124 Error::Database(Box::new(DatabaseError::new(message)))
125}