Skip to main content
Glama
convert_v8.rs5.78 kB
use std::{ backtrace::Backtrace, borrow::Cow, fmt, }; use anyhow::Context as _; use deno_core::{ serde_v8, v8, }; use serde::{ Deserialize, Serialize, }; use strum::IntoStaticStr; use crate::strings; pub(crate) trait FromV8 { type Output: Sized; fn from_v8<'s>( scope: &mut v8::PinScope<'s, '_>, input: v8::Local<'s, v8::Value>, ) -> anyhow::Result<Self::Output>; } impl<T: for<'de> Deserialize<'de>> FromV8 for T { type Output = Self; fn from_v8<'s>( scope: &mut v8::PinScope<'s, '_>, input: v8::Local<'s, v8::Value>, ) -> anyhow::Result<Self> { Ok(serde_v8::from_v8(scope, input)?) } } pub(crate) trait ToV8: Sized { fn to_v8<'s>( self, scope: &mut v8::PinScope<'s, '_>, ) -> anyhow::Result<v8::Local<'s, v8::Value>>; } impl<T: Serialize> ToV8 for T { fn to_v8<'a>( self, scope: &mut v8::PinScope<'a, '_>, ) -> anyhow::Result<v8::Local<'a, v8::Value>> { Ok(serde_v8::to_v8(scope, self)?) } } #[derive(Copy, Clone, Debug, IntoStaticStr)] #[allow(dead_code)] #[allow(clippy::enum_variant_names)] pub(crate) enum DOMExceptionName { IndexSizeError, HierarchyRequestError, WrongDocumentError, InvalidCharacterError, NoModificationAllowedError, NotFoundError, NotSupportedError, InUseAttributeError, InvalidStateError, SyntaxError, InvalidModificationError, NamespaceError, InvalidAccessError, TypeMismatchError, SecurityError, NetworkError, AbortError, URLMismatchError, QuotaExceededError, TimeoutError, InvalidNodeTypeError, DataCloneError, EncodingError, NotReadableError, UnknownError, ConstraintError, DataError, TransactionInactiveError, ReadOnlyError, VersionError, OperationError, NotAllowedError, OptOutError, } #[derive(Debug)] pub(crate) enum JsException { DOMException(DOMException), TypeError(TypeError), } #[derive(Debug)] pub(crate) struct DOMException { pub message: Cow<'static, str>, pub name: DOMExceptionName, } impl DOMException { pub(crate) fn new(message: impl Into<Cow<'static, str>>, name: DOMExceptionName) -> Self { Self { message: message.into(), name, } } } impl ToV8 for DOMException { fn to_v8<'s>( self, scope: &mut v8::PinScope<'s, '_>, ) -> anyhow::Result<v8::Local<'s, v8::Value>> { // TODO: do not read off of the global object, which is mutable by JS let dom_exception_str = strings::DOMException.create(scope)?; let global = scope.get_current_context().global(scope); let dom_exception_class = global .get(scope, dom_exception_str.into()) .context("missing DOMException")? .try_cast::<v8::Function>() .context("DOMException isn't v8::Function")?; let message = v8::String::new(scope, &self.message).context("failed to create string")?; let name = v8::String::new_from_utf8( scope, <&str>::from(self.name).as_bytes(), v8::NewStringType::Internalized, ) .context("failed to create string")?; Ok(dom_exception_class .new_instance(scope, &[message.into(), name.into()]) .context("failed to create DOMException")? .into()) } } #[derive(Debug)] pub(crate) struct TypeError { pub message: Cow<'static, str>, } impl TypeError { pub(crate) fn new(message: impl Into<Cow<'static, str>>) -> Self { Self { message: message.into(), } } } impl ToV8 for TypeError { fn to_v8<'s>( self, scope: &mut v8::PinScope<'s, '_>, ) -> anyhow::Result<v8::Local<'s, v8::Value>> { let message = v8::String::new(scope, &self.message).context("failed to create string")?; Ok(v8::Exception::type_error(scope, message)) } } impl From<DOMException> for anyhow::Error { fn from(e: DOMException) -> Self { anyhow::Error::new(JsException::DOMException(e)) } } impl From<TypeError> for anyhow::Error { fn from(e: TypeError) -> Self { anyhow::Error::new(JsException::TypeError(e)) } } impl fmt::Display for JsException { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { JsException::DOMException(e) => write!(f, "{:?}: {}", e.name, e.message), JsException::TypeError(e) => write!(f, "TypeError: {}", e.message), } } } impl std::error::Error for JsException { // Turn off native backtraces for JsExceptions as these are never meant to // be reported, but rather forwarded to V8 fn provide<'a>(&'a self, request: &mut std::error::Request<'a>) { static NO_BACKTRACE: Backtrace = Backtrace::disabled(); request.provide_ref(&NO_BACKTRACE); } } impl ToV8 for JsException { fn to_v8<'s>( self, scope: &mut v8::PinScope<'s, '_>, ) -> anyhow::Result<v8::Local<'s, v8::Value>> { match self { JsException::DOMException(e) => e.to_v8(scope), JsException::TypeError(e) => e.to_v8(scope), } } } /// Converts to an ArrayBuffer in JS, unlike [`serde_v8::ToJsBuffer`] which /// becomes a Uint8Array pub(crate) struct ArrayBuffer(pub Vec<u8>); impl ToV8 for ArrayBuffer { fn to_v8<'s>( self, scope: &mut v8::PinScope<'s, '_>, ) -> anyhow::Result<v8::Local<'s, v8::Value>> { let backing_store = v8::ArrayBuffer::new_backing_store_from_vec(self.0).make_shared(); let array_buffer = v8::ArrayBuffer::with_backing_store(scope, &backing_store); Ok(array_buffer.into()) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server