Skip to content

Commit

Permalink
Merge pull request #89 from Herschel/error
Browse files Browse the repository at this point in the history
Implement Error and DOMException. Add patterns for error handling.
  • Loading branch information
koute committed Feb 1, 2018
2 parents 61999ee + 80b559d commit 322afa8
Show file tree
Hide file tree
Showing 7 changed files with 508 additions and 107 deletions.
12 changes: 11 additions & 1 deletion src/lib.rs
Expand Up @@ -163,7 +163,17 @@ pub mod web {

/// A module containing error types.
pub mod error {
pub use webapi::node::NotFoundError;
pub use webapi::dom_exception::{
IDomException,
DomException,
ConcreteException,
HierarchyRequestError,
InvalidAccessError,
NotFoundError,
SecurityError,
SyntaxError,
};
pub use webapi::error::{IError, Error};
}

/// A module containing HTML DOM elements.
Expand Down
155 changes: 155 additions & 0 deletions src/webapi/dom_exception.rs
@@ -0,0 +1,155 @@
use webcore::value::Reference;
use webapi::error::{IError, Error};

/// The `IDomException` interface represents an abnormal event which occurs as the result of
/// calling a web API.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
pub trait IDomException: IError {}

/// A reference to a JavaScript `DOMException` object.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
pub struct DomException( Reference );

// DOMException specification:
// https://heycam.github.io/webidl/#idl-DOMException

impl IError for DomException {}
impl IDomException for DomException {}

reference_boilerplate! {
DomException,
instanceof DOMException
convertible to Error
}
error_boilerplate! { DomException }

/// A trait representing a concrete DOMException.
pub trait ConcreteException: IDomException {
/// A string representing the error type.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DOMException/name)
const ERROR_NAME: &'static str;
}

// To safely cast from a reference to a specific DomException, we must check that the object is an
// instance of DOMException, and that the error name matches.
impl<T: ConcreteException + ::webcore::value::FromReferenceUnchecked> ::webcore::value::FromReference for T {
#[inline]
fn from_reference( reference: Reference ) -> Option< Self > {
if instanceof!( reference, DOMException ) &&
js!( return @{reference.clone()}.name; ) == Self::ERROR_NAME {
unsafe {
Some( Self::from_reference_unchecked( reference ) )
}
} else {
None
}
}
}

/// Occurs when an operation would result in an incorrect node tree.
pub struct HierarchyRequestError( Reference );

impl IError for HierarchyRequestError {}
impl IDomException for HierarchyRequestError {}
impl ConcreteException for HierarchyRequestError {
const ERROR_NAME: &'static str = "HierarchyRequestError";
}

reference_boilerplate! {
HierarchyRequestError,
convertible to Error
convertible to DomException
}
error_boilerplate! { HierarchyRequestError }

/// Occurs when an object does not support an operation or argument.
pub struct InvalidAccessError( Reference );

impl IError for InvalidAccessError {}
impl IDomException for InvalidAccessError {}
impl ConcreteException for InvalidAccessError {
const ERROR_NAME: &'static str = "InvalidAccessError";
}

reference_boilerplate! {
InvalidAccessError,
convertible to Error
convertible to DomException
}
error_boilerplate! { InvalidAccessError }

/// Occurs when the specified object cannot be found.
pub struct NotFoundError( Reference );

impl IError for NotFoundError {}
impl IDomException for NotFoundError {}
impl ConcreteException for NotFoundError {
const ERROR_NAME: &'static str = "NotFoundError";
}

reference_boilerplate! {
NotFoundError,
convertible to Error
convertible to DomException
}
error_boilerplate! { NotFoundError }

/// Occurs when the requested operation is insecure.
pub struct SecurityError( Reference );

impl IError for SecurityError {}
impl IDomException for SecurityError {}
impl ConcreteException for SecurityError {
const ERROR_NAME: &'static str = "SecurityError";
}

reference_boilerplate! {
SecurityError,
convertible to Error
convertible to DomException
}
error_boilerplate! { SecurityError }

/// Occurs when an argument does not match the expected pattern.
pub struct SyntaxError( Reference );

impl IError for SyntaxError {}
impl IDomException for SyntaxError {}
impl ConcreteException for SyntaxError {
const ERROR_NAME: &'static str = "SyntaxError";
}

reference_boilerplate! {
SyntaxError,
convertible to Error
convertible to DomException
}
error_boilerplate! { SyntaxError }

#[cfg(all(test, feature = "web_test"))]
mod test {
use super::*;
use webcore::try_from::TryInto;

fn new_dom_exception(message: &str, name: &str) -> DomException {
js!(
return new DOMException(@{message}, @{name});
).try_into().unwrap()
}

#[test]
fn test_error() {
// Successful downcast.
let err: DomException = new_dom_exception("foo", HierarchyRequestError::ERROR_NAME);
let err: HierarchyRequestError = err.try_into().expect("Expected HierarchyRequestError");
assert_eq!(err.name(), HierarchyRequestError::ERROR_NAME);

// Failed downcast.
let err: DomException = new_dom_exception("foo", SecurityError::ERROR_NAME);
let err: Result<SyntaxError, _> = err.try_into();
assert!(err.is_err());
}
}
67 changes: 67 additions & 0 deletions src/webapi/error.rs
@@ -0,0 +1,67 @@
use webcore::value::{Value, Reference};
use webcore::try_from::{TryFrom, TryInto};

/// Represents the JavaScript `Error` interface. An `Error` is thrown whenever a run-time error
/// occurs.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
pub trait IError: AsRef< Reference > + TryFrom< Value > {
/// Returns a human-readable description of the error.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message)
#[inline]
fn message( &self ) -> String {
js!(
return @{self.as_ref()}.message;
).try_into().unwrap()
}

/// Returns a name specifiying the type of error.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/name)
#[inline]
fn name( &self ) -> String {
js!(
return @{self.as_ref()}.name;
).try_into().unwrap()
}
}

/// A reference to a JavaScript `Error` object. An `Error` is thrown whenever a run-time error
/// occurs.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
pub struct Error( Reference );

// Error specification:
// https://www.ecma-international.org/ecma-262/6.0/#sec-error-objects

impl IError for Error {}

reference_boilerplate! {
Error,
instanceof Error
}
error_boilerplate! { Error }

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_error() {
use ::std::fmt::Write;

let error: Error = js!(
return new Error("foo");
).try_into().unwrap();

assert_eq!(error.name(), "Error");
assert_eq!(error.message(), "foo");

let mut text = String::new();
write!(&mut text, "{}", error).unwrap();
assert_eq!(&text, "Error: foo");
assert_eq!(::std::error::Error::description(&error), "Error");
}
}
2 changes: 2 additions & 0 deletions src/webapi/mod.rs
Expand Up @@ -28,3 +28,5 @@ pub mod history;
pub mod web_socket;
pub mod rendering_context;
pub mod mutation_observer;
pub mod error;
pub mod dom_exception;

0 comments on commit 322afa8

Please sign in to comment.