/stdweb

A standard library for the client-side Web

Primary LanguageRustApache License 2.0Apache-2.0

Build Status Join the chat at https://gitter.im/stdweb-rs/stdweb

A standard library for the client-side Web

Documentation

The goal of this crate is to provide Rust bindings to the Web APIs and to allow a high degree of interoperability between Rust and JavaScript.

Donate

Become a patron

Patrons

This software was brought to you thanks to these wonderful people:

  • Ben Berman
  • Stephen Sugden

Thank you!

Examples

You can directly embed JavaScript code into Rust:

let message = "Hello, 世界!";
let result = js! {
    alert( @{message} );
    return 2 + 2 * 2;
};

println!( "2 + 2 * 2 = {:?}", result );

Closures are also supported:

let print_hello = |name: String| {
    println!( "Hello, {}!", name );
};

js! {
    var print_hello = @{print_hello};
    print_hello( "Bob" );
    print_hello.drop(); // Necessary to clean up the closure on Rust's side.
}

You can also pass arbitrary structures thanks to serde:

#[derive(Serialize)]
struct Person {
    name: String,
    age: i32
}

js_serializable!( Person );

js! {
    var person = @{person};
    console.log( person.name + " is " + person.age + " years old." );
};

This crate also exposes a number of Web APIs, for example:

let button = document().query_selector( "#hide-button" ).unwrap().unwrap();
button.add_event_listener( move |_: ClickEvent| {
    for anchor in document().query_selector_all( "#main a" ) {
        js!( @{anchor}.style = "display: none;"; );
    }
});

Exposing Rust functions to JavaScript is supported too:

#[js_export]
fn hash( string: String ) -> String {
    let mut hasher = Sha1::new();
    hasher.update( string.as_bytes() );
    hasher.digest().to_string()
}

Then you can do this from Node.js:

var hasher = require( "hasher.js" ); // Where `hasher.js` is generated from Rust code.
console.log( hasher.hash( "Hello world!" ) );

Or you can take the same .js file and use it in a web browser:

<script src="hasher.js"></script>
<script>
    Rust.hasher.then( function( hasher ) {
        console.log( hasher.hash( "Hello world!" ) );
    });
</script>

If you're using Parcel you can also use our experimental Parcel plugin; first do this in your existing Parcel project:

$ npm install --save parcel-plugin-cargo-web

And then simply:

import hasher from "./hasher/Cargo.toml";
console.log( hasher.hash( "Hello world!" ) );

Design goals

  • Expose a full suite of Web APIs as exposed by web browsers.
  • Try to follow the original JavaScript conventions and structure as much as possible, except in cases where doing otherwise results in a clearly superior design.
  • Be a building block from which higher level frameworks and libraries can be built.
  • Make it convenient and easy to embed JavaScript code directly into Rust and to marshal data between the two.
  • Integrate with the wider Rust ecosystem, e.g. support marshaling of structs which implement serde's Serializable.
  • Put Rust in the driver's seat where a non-trivial Web application can be written without touching JavaScript at all.
  • Allow Rust to take part in the upcoming WebAssembly (re)volution.
  • Make it possible to trivially create standalone libraries which are easily callable from JavaScript.

Getting started

Take a look at some of the examples:

  • examples/minimal - a totally minimal example which calls alert
  • examples/todomvc - a naively implemented TodoMVC application; shows how to call into the DOM
  • examples/hasher - shows how to export Rust functions to JavaScript and how to call them from a vanilla web browser environment or from Nodejs
  • examples/hasher-parcel - shows how to import and call exported Rust functions in a Parcel project
  • pinky-web - an NES emulator; you can play with the precompiled version here

Running the examples

  1. Install cargo-web:

    $ cargo install -f cargo-web
    
  2. Go into examples/todomvc and start the example using one of these commands:

    • Compile to WebAssembly using Rust's native WebAssembly backend (requires Rust nightly!):

      $ cargo web start --target=wasm32-unknown-unknown
      
    • Compile to asm.js using Emscripten:

      $ cargo web start --target=asmjs-unknown-emscripten
      
    • Compile to WebAssembly using Emscripten:

      $ cargo web start --target=wasm32-unknown-emscripten
      
  3. Visit http://localhost:8000 with your browser.

For the *-emscripten targets cargo-web is not necessary, however the native wasm32-unknown-unknown which doesn't need Emscripten requires cargo-web to work!

Changelog

  • 0.4.1

    • Support for newest nightly Rust on wasm32-unknown-unknown
    • Exposed SocketBinaryType enum
    • New canvas APIs:
      • Numerous new methods for CanvasRenderingContext2d
      • New types: CanvasGradient, CanvasPattern, CanvasStyle, ImageData, TextMetrics
    • New error types: IndexSizeError, NotSupportedError, TypeError
  • 0.4

    • (breaking change) Removed Array and Object variants from Value; these are now treated as References
    • (breaking change) The Value has an extra variant: Symbol
    • (breaking change) Removed:
      • InputElement::set_kind
      • InputElement::files
    • (breaking change) Renamed:
      • KeydownEvent -> KeyDownEvent
      • KeyupEvent -> KeyUpEvent
      • KeypressEvent -> KeyPressEvent
      • ReadyState -> FileReaderReadyState
      • InputElement::value -> InputElement::raw_value
      • InputElement::set_value -> InputElement::set_raw_value
    • (breaking change) ArrayBuffer::new now takes an u64 argument
    • (breaking change) InputElement::set_raw_value now takes &str instead of Into< Value >
    • (breaking change) Changed return types:
      • Every method which returned usize now returns u32
      • INode::remove_child now returns Node in the Ok case
      • The following now return an u64:
        • ArrayBuffer::len
      • The following now return an i32 instead of f64:
        • IMouseEvent::client_x
        • IMouseEvent::client_y
        • IMouseEvent::movement_x
        • IMouseEvent::movement_y
        • IMouseEvent::screen_x
        • IMouseEvent::screen_y
      • The following now return a Result:
        • INode::insert_before
        • INode::replace_child
        • INode::clone_node
        • StringMap::insert
        • TokenList::add
        • TokenList::remove
        • Document::create_element
        • IEventTarget::dispatch_event
        • FileReader::read_as_text
        • FileReader::read_as_array_buffer
        • FileReader::read_as_text
        • History::replace_state
        • History::go
        • History::back
        • History::forward
        • Location::href
        • Location::hash
        • CanvasElement::to_data_url
        • CanvasElement::to_blob
        • ArrayBuffer::new
      • INode::base_uri now returns a String instead of Option< String >
      • InputElement::raw_value now returns a String instead of Value
    • (breaking change) INode::inner_text was moved to IHtmlElement::inner_text
    • (breaking change) Document::query_selector and Document::query_selector_all were moved to IParentNode
    • (breaking change) IElement::query_selector and IElement::query_selector_all were moved to IParentNode
    • (breaking change) Document::get_element_by_id was moved to INonElementParentNode
    • (breaking change) A blanket impl for converting between arbitrary reference-like objects using TryFrom/TryInto has been removed
    • When building using a recent cargo-web it's not necessary to call stdweb::initialize nor stdweb::event_loop anymore
    • Support for cdylib crates on wasm32-unknown-unknown
    • New bindings:
      • XmlHttpRequest
      • WebSocket
      • MutationObserver
      • History
      • TextAreaElement
      • CanvasElement
    • New event types:
      • MouseDownEvent
      • MouseUpEvent
      • MouseMoveEvent
      • PopStateEvent
      • ResizeEvent
      • ReadyStateChange
      • SocketCloseEvent
      • SocketErrorEvent
      • SocketOpenEvent
      • SocketMessageEvent
    • Initial support for the Canvas APIs
    • New traits: ReferenceType and InstanceOf
    • Add #[derive(ReferenceType)] in stdweb-derive crate; it's now possible to define custom API bindings outside of stdweb
    • Add #[js_export] procedural attribute (wasm32-unknown-unknown only)
    • Add DomException and subtypes for passing around JavaScript exceptions
    • IElement now inherits from INode
    • Every interface now inherits from ReferenceType
    • Add stdweb::traits module to act as a prelude for use-ing all of our interface traits
    • Add console! macro
    • Most types now implement PartialEq and Eq
  • 0.3

    • (breaking change) Deleted ErrorEvent methods
    • (breaking change) Renamed:
      • LoadEvent -> ResourceLoadEvent
      • AbortEvent -> ResourceAbortEvent
      • ErrorEvent -> ResourceErrorEvent
    • Add UnsafeTypedArray for zero cost slice passing to js!
    • Add Once for passing FnOnce closures to js!

License

Licensed under either of

at your option.

Snippets of documentation which come from Mozilla Developer Network are covered under the CC-BY-SA, version 2.5 or later.

Contributing

See CONTRIBUTING.md