feat: patch js-wasm
This commit is contained in:
6
__wasm/js-wasm/helloworld/js-wasm/.gitignore
vendored
Normal file
6
__wasm/js-wasm/helloworld/js-wasm/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.vscode
|
||||
target
|
||||
Cargo.lock
|
||||
tmp
|
||||
dist
|
||||
node_modules
|
||||
1
__wasm/js-wasm/helloworld/js-wasm/CNAME
Normal file
1
__wasm/js-wasm/helloworld/js-wasm/CNAME
Normal file
@@ -0,0 +1 @@
|
||||
wasm.js.org
|
||||
201
__wasm/js-wasm/helloworld/js-wasm/LICENSE-APACHE
Normal file
201
__wasm/js-wasm/helloworld/js-wasm/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (c) 2019 Richard Anaya
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
8
__wasm/js-wasm/helloworld/js-wasm/LICENSE-MIT
Normal file
8
__wasm/js-wasm/helloworld/js-wasm/LICENSE-MIT
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
Copyright 2020 Richard Anaya
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
11
__wasm/js-wasm/helloworld/js-wasm/Makefile
Normal file
11
__wasm/js-wasm/helloworld/js-wasm/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
build:
|
||||
npm run build
|
||||
build-rs:
|
||||
cd crates/js-bindgen/ && cargo run -- --language c ../../bindings/web_console.yaml > ../../headers/web_console.h
|
||||
cd crates/js-bindgen/ && cargo run -- --language c ../../bindings/web_canvas.yaml > ../../headers/web_canvas.h
|
||||
cd crates/js-bindgen/ && cargo run -- --language c ../../bindings/web_dom.yaml > ../../headers/web_dom.h
|
||||
cd crates/js-bindgen/ && cargo run -- --language rust ../../bindings/web_console.yaml > ../../crates/web_console/src/lib.rs
|
||||
cd crates/js-bindgen/ && cargo run -- --language assemblyscript ../../bindings/web_console.yaml > ../../assemblyscript/web_console.ts
|
||||
cd crates/js-bindgen/ && cargo run -- --language assemblyscript ../../bindings/web_dom.yaml > ../../assemblyscript/web_dom.ts
|
||||
serve: build
|
||||
python3 -m http.server
|
||||
100
__wasm/js-wasm/helloworld/js-wasm/README.md
Normal file
100
__wasm/js-wasm/helloworld/js-wasm/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# js-wasm
|
||||
<a href="https://docs.rs/js"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
*JavaScript and WebAssembly should be a joy to use together.*
|
||||
|
||||
This project aims to provide a simple, easy to learn, technology-agnostic way bridge the Rust and Javascript using an extremely minimal setup with out-of-box cargo compilation tools.
|
||||
|
||||
See a [demo](https://richardanaya.github.io/js-wasm/examples/snake/index.html) of it working!
|
||||
|
||||
# How It Works?
|
||||
```bash
|
||||
cargo new helloworld --lib
|
||||
cd helloworld
|
||||
cargo add js
|
||||
vim src/lib.rs
|
||||
```
|
||||
```rust
|
||||
use js::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() {
|
||||
// let's dynamically create a javascript function we can invoke to write logs
|
||||
let fn_log = js!("function(strPtr,strLen){
|
||||
console.log(this.readUtf8FromMemory(strPtr,strLen));
|
||||
}");
|
||||
let msg = "Hello World!";
|
||||
fn_log.invoke_2(msg.as_ptr() as u32, msg.len() as u32);
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
vim index.html
|
||||
```
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://unpkg.com/js-wasm/js-wasm.js"></script>
|
||||
<script type="application/wasm" src="helloworld.wasm"></script>
|
||||
</head>
|
||||
<body>
|
||||
Open my console.
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
```bash
|
||||
vim Cargo.toml
|
||||
```
|
||||
```toml
|
||||
# add these lines for WebAssembly to end of Cargo.toml
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
```
|
||||
```bash
|
||||
cargo build --target wasm32-unknown-unknown --release
|
||||
cp target/wasm32-unknown-unknown/release/helloworld.wasm .
|
||||
python3 -m http.server
|
||||
|
||||
# open http://localhost:8000 in browser
|
||||
# right click, inspect, look at message in console
|
||||
```
|
||||
|
||||
Full example is [here](https://github.com/richardanaya/js-wasm/tree/master/examples/helloworld).
|
||||
|
||||
|
||||
# details
|
||||
In your JS function context `this` contains several functions handle most issues you'll encounter. It's probably easiest to look at examples to figure out how they are used.
|
||||
|
||||
| Name | Description |
|
||||
| ------------- | ------------- |
|
||||
| `readUtf8FromMemory(start,length)` | Extract utf-8 text from your program's memory. |
|
||||
| `readUtf16FromMemory(start,length)` | Extract utf-16 text from your program's memory. |
|
||||
| `writeUtf8ToMemory(str)` | Write utf-8 to a memory location you are sure it should go. |
|
||||
| `readUint8ArrayFromMemory(start)` | Read a list of uint8 from a pointer to a location of a number of elements, followed by a pointer to bytes in memory. |
|
||||
| `storeObject(object)` | Store an object in your context for later reference, get a handle you can give to WebAssembly. |
|
||||
| `getObject(handle)` | Retreive and object from your context with a handle. |
|
||||
| `releaseObject(handle)` | Release a stored object so it's memory can be freed. |
|
||||
| `createCallback(callbackHandle)` | Creates a callback function that will pass its arguments to the associated WebAssembly function represented by the handle. |
|
||||
| `module` | Get access to your program so you can call methods on it. |
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `js-wasm` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
52
__wasm/js-wasm/helloworld/js-wasm/assemblyscript/js-wasm.ts
Normal file
52
__wasm/js-wasm/helloworld/js-wasm/assemblyscript/js-wasm.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
|
||||
@external("env", "js_register_function")
|
||||
export declare function js_register_function(codePtr: f64, codeLen: f64, utfByeLen: f64): f64
|
||||
@external("env", "js_invoke_function")
|
||||
export declare function js_invoke_function(fn: f64, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64, g: f64, h: f64, i: f64, j: f64): f64
|
||||
@external("env", "js_release")
|
||||
export declare function js_release(ref: f64): void
|
||||
|
||||
export function js_invoke_function_0(fn: f64): void {
|
||||
js_invoke_function(fn,0,0,0,0,0,0,0,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_1(fn: f64, a: f64): void {
|
||||
js_invoke_function(fn,a,0,0,0,0,0,0,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_2(fn: f64, a: f64, b: f64): void {
|
||||
js_invoke_function(fn,a,b,0,0,0,0,0,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_3(fn: f64, a: f64, b: f64, c: f64): void {
|
||||
js_invoke_function(fn,a,b,c,0,0,0,0,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_4(fn: f64, a: f64, b: f64, c: f64, d: f64): void {
|
||||
js_invoke_function(fn,a,b,c,d,0,0,0,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_5(fn: f64, a: f64, b: f64, c: f64, d: f64, e: f64): void {
|
||||
js_invoke_function(fn,a,b,c,d,e,0,0,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_6(fn: f64, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64): void {
|
||||
js_invoke_function(fn,a,b,c,d,e,f,0,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_7(fn: f64, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64, g: f64): void {
|
||||
js_invoke_function(fn,a,b,c,d,e,f,g,0,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_8(fn: f64, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64, g: f64, h: f64): void {
|
||||
js_invoke_function(fn,a,b,c,d,e,f,g,h,0,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_9(fn: f64, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64, g: f64, h: f64, i: f64): void {
|
||||
js_invoke_function(fn,a,b,c,d,e,f,g,h,i,0);
|
||||
}
|
||||
|
||||
export function js_invoke_function_10(fn: f64, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64, g: f64, h: f64, i: f64, j: f64): void {
|
||||
js_invoke_function(fn,a,b,c,d,e,f,g,h,i,j);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import * as jswasm from "./js-wasm"
|
||||
let console_clear_fn:f64 = 0;
|
||||
export function console_clear() : void {
|
||||
if( console_clear_fn === 0) {
|
||||
const code = `function(){ console.clear(); }`;
|
||||
console_clear_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_0(console_clear_fn);
|
||||
}
|
||||
|
||||
let console_log_fn:f64 = 0;
|
||||
export function console_log(msg: string) : void {
|
||||
const a0: f64 = <f64>changetype<usize>(msg);
|
||||
const a1: f64 = msg.length*2;
|
||||
if( console_log_fn === 0) {
|
||||
const code = `function(msgPtr,msgLen){ console.log(this.readUtf16FromMemory(msgPtr,msgLen)); }`;
|
||||
console_log_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_2(console_log_fn, a0, a1);
|
||||
}
|
||||
|
||||
let console_warning_fn:f64 = 0;
|
||||
export function console_warning(msg: string) : void {
|
||||
const a0: f64 = <f64>changetype<usize>(msg);
|
||||
const a1: f64 = msg.length*2;
|
||||
if( console_warning_fn === 0) {
|
||||
const code = `function(msgPtr,msgLen){ console.warn(this.readUtf16FromMemory(msgPtr,msgLen)); }`;
|
||||
console_warning_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_2(console_warning_fn, a0, a1);
|
||||
}
|
||||
|
||||
let console_error_fn:f64 = 0;
|
||||
export function console_error(msg: string) : void {
|
||||
const a0: f64 = <f64>changetype<usize>(msg);
|
||||
const a1: f64 = msg.length*2;
|
||||
if( console_error_fn === 0) {
|
||||
const code = `function(msgPtr,msgLen){ console.error(this.readUtf16FromMemory(msgPtr,msgLen)); }`;
|
||||
console_error_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_2(console_error_fn, a0, a1);
|
||||
}
|
||||
|
||||
let console_time_fn:f64 = 0;
|
||||
export function console_time(msg: string) : void {
|
||||
const a0: f64 = <f64>changetype<usize>(msg);
|
||||
const a1: f64 = msg.length*2;
|
||||
if( console_time_fn === 0) {
|
||||
const code = `function(msgPtr,msgLen){ console.time(this.readUtf16FromMemory(msgPtr,msgLen)); }`;
|
||||
console_time_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_2(console_time_fn, a0, a1);
|
||||
}
|
||||
|
||||
let console_time_end_fn:f64 = 0;
|
||||
export function console_time_end(msg: string) : void {
|
||||
const a0: f64 = <f64>changetype<usize>(msg);
|
||||
const a1: f64 = msg.length*2;
|
||||
if( console_time_end_fn === 0) {
|
||||
const code = `function(msgPtr,msgLen){ console.timeEnd(this.readUtf16FromMemory(msgPtr,msgLen)); }`;
|
||||
console_time_end_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_2(console_time_end_fn, a0, a1);
|
||||
}
|
||||
|
||||
12
__wasm/js-wasm/helloworld/js-wasm/assemblyscript/web_dom.ts
Normal file
12
__wasm/js-wasm/helloworld/js-wasm/assemblyscript/web_dom.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as jswasm from "./js-wasm"
|
||||
let document_get_element_by_id_fn:f64 = 0;
|
||||
export function document_get_element_by_id(id: string) : f64 {
|
||||
const a0: f64 = <f64>changetype<usize>(id);
|
||||
const a1: f64 = id.length*2;
|
||||
if( document_get_element_by_id_fn === 0) {
|
||||
const code = `function(idPtr,idLen){ return this.storeObject(document.getElementById(this.readUtf16FromMemory(idPtr,idLen))); }`;
|
||||
document_get_element_by_id_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
return jswasm.js_invoke_function_2(document_get_element_by_id_fn, a0, a1);
|
||||
}
|
||||
|
||||
37
__wasm/js-wasm/helloworld/js-wasm/bindings/web_canvas.yaml
Normal file
37
__wasm/js-wasm/helloworld/js-wasm/bindings/web_canvas.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
- namespace: canvas
|
||||
class: CanvasElement
|
||||
parameter_self: el
|
||||
method:
|
||||
- name: getContext
|
||||
parameters:
|
||||
- name: contextType
|
||||
parameter_type: string
|
||||
|
||||
- namespace: canvas
|
||||
class: CanvasContext
|
||||
parameter_self: ctx
|
||||
properties:
|
||||
- name: fillStyle
|
||||
property_type: string
|
||||
methods:
|
||||
- name: fillRect
|
||||
parameters:
|
||||
- name: x
|
||||
parameter_type: f64
|
||||
- name: "y"
|
||||
parameter_type: f64
|
||||
- name: height
|
||||
parameter_type: f64
|
||||
- name: width
|
||||
parameter_type: f64
|
||||
- name: clearRect
|
||||
parameters:
|
||||
- name: x
|
||||
parameter_type: f64
|
||||
- name: "y"
|
||||
parameter_type: f64
|
||||
- name: height
|
||||
parameter_type: f64
|
||||
- name: width
|
||||
parameter_type: f64
|
||||
25
__wasm/js-wasm/helloworld/js-wasm/bindings/web_console.yaml
Normal file
25
__wasm/js-wasm/helloworld/js-wasm/bindings/web_console.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
- namespace: console
|
||||
functions:
|
||||
- name: clear
|
||||
- name: log
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: warn
|
||||
friendly_name: warning
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: error
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: time
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: timeEnd
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
8
__wasm/js-wasm/helloworld/js-wasm/bindings/web_dom.yaml
Normal file
8
__wasm/js-wasm/helloworld/js-wasm/bindings/web_dom.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
- namespace: document
|
||||
functions:
|
||||
- name: getElementById
|
||||
parameters:
|
||||
- name: id
|
||||
parameter_type: string
|
||||
output: object
|
||||
14
__wasm/js-wasm/helloworld/js-wasm/crates/callback/Cargo.toml
Normal file
14
__wasm/js-wasm/helloworld/js-wasm/crates/callback/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "callback"
|
||||
version = "0.5.4"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2021"
|
||||
description = "A library for handling callbacks in WebAssembly"
|
||||
license = "MIT OR Apache-2.0"
|
||||
categories = ["wasm", "no-std"]
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
rust-version="1.62"
|
||||
|
||||
[dependencies]
|
||||
spin = "0"
|
||||
20
__wasm/js-wasm/helloworld/js-wasm/crates/callback/README.md
Normal file
20
__wasm/js-wasm/helloworld/js-wasm/crates/callback/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# callback
|
||||
|
||||
A library for handling callbacks in WebAssembly.
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in 'callback' by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
791
__wasm/js-wasm/helloworld/js-wasm/crates/callback/src/lib.rs
Normal file
791
__wasm/js-wasm/helloworld/js-wasm/crates/callback/src/lib.rs
Normal file
@@ -0,0 +1,791 @@
|
||||
#![no_std]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
extern crate alloc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll, Waker},
|
||||
};
|
||||
use spin::{Mutex, MutexGuard};
|
||||
|
||||
pub enum CallbackHandler {
|
||||
Callback0(Box<dyn FnMut() + Send + 'static>),
|
||||
Callback1(Box<dyn FnMut(f64) + Send + 'static>),
|
||||
Callback2(Box<dyn FnMut(f64, f64) + Send + 'static>),
|
||||
Callback3(Box<dyn FnMut(f64, f64, f64) + Send + 'static>),
|
||||
Callback4(Box<dyn FnMut(f64, f64, f64, f64) + Send + 'static>),
|
||||
Callback5(Box<dyn FnMut(f64, f64, f64, f64, f64) + Send + 'static>),
|
||||
Callback6(Box<dyn FnMut(f64, f64, f64, f64, f64, f64) + Send + 'static>),
|
||||
Callback7(Box<dyn FnMut(f64, f64, f64, f64, f64, f64, f64) + Send + 'static>),
|
||||
Callback8(Box<dyn FnMut(f64, f64, f64, f64, f64, f64, f64, f64) + Send + 'static>),
|
||||
Callback9(Box<dyn FnMut(f64, f64, f64, f64, f64, f64, f64, f64, f64) + Send + 'static>),
|
||||
Callback10(Box<dyn FnMut(f64, f64, f64, f64, f64, f64, f64, f64, f64, f64) + Send + 'static>),
|
||||
}
|
||||
|
||||
type CallbackHandle = f64;
|
||||
|
||||
pub struct CallbackManager {
|
||||
cur_id: CallbackHandle,
|
||||
pub keys: Vec<CallbackHandle>,
|
||||
pub handlers: Vec<Arc<Mutex<CallbackHandler>>>,
|
||||
}
|
||||
|
||||
fn get_callbacks() -> MutexGuard<'static, CallbackManager> {
|
||||
static SINGLETON: Mutex<CallbackManager> = {
|
||||
Mutex::new(CallbackManager {
|
||||
cur_id: 0.0,
|
||||
keys: Vec::new(),
|
||||
handlers: Vec::new(),
|
||||
})
|
||||
};
|
||||
SINGLETON.lock()
|
||||
}
|
||||
|
||||
pub fn get_callback(id: CallbackHandle) -> Option<Arc<Mutex<CallbackHandler>>> {
|
||||
let cbs = get_callbacks();
|
||||
let index = cbs.keys.iter().position(|&r| r == id);
|
||||
if let Some(i) = index {
|
||||
let handler_ref = cbs.handlers.get(i).unwrap().clone();
|
||||
core::mem::drop(cbs);
|
||||
Some(handler_ref)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_callback(id: CallbackHandle) {
|
||||
let mut cbs = get_callbacks();
|
||||
let index = cbs.keys.iter().position(|&r| r == id);
|
||||
if let Some(i) = index {
|
||||
cbs.keys.remove(i);
|
||||
cbs.handlers.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_callback(cb: CallbackHandler) -> f64 {
|
||||
let mut h = get_callbacks();
|
||||
h.cur_id += 1.0;
|
||||
let id = h.cur_id;
|
||||
h.keys.push(id);
|
||||
h.handlers.push(Arc::new(Mutex::new(cb)));
|
||||
id
|
||||
}
|
||||
|
||||
pub fn create_callback_0(cb: impl FnMut() + Send + 'static) -> f64 {
|
||||
create_callback(CallbackHandler::Callback0(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_1(cb: impl FnMut(f64) + Send + 'static) -> f64 {
|
||||
create_callback(CallbackHandler::Callback1(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_2(cb: impl FnMut(f64, f64) + Send + 'static) -> f64 {
|
||||
create_callback(CallbackHandler::Callback2(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_3(cb: impl FnMut(f64, f64, f64) + Send + 'static) -> f64 {
|
||||
create_callback(CallbackHandler::Callback3(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_4(cb: impl FnMut(f64, f64, f64, f64) + Send + 'static) -> f64 {
|
||||
create_callback(CallbackHandler::Callback4(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_5(cb: impl FnMut(f64, f64, f64, f64, f64) + Send + 'static) -> f64 {
|
||||
create_callback(CallbackHandler::Callback5(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_6(cb: impl FnMut(f64, f64, f64, f64, f64, f64) + Send + 'static) -> f64 {
|
||||
create_callback(CallbackHandler::Callback6(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_7(
|
||||
cb: impl FnMut(f64, f64, f64, f64, f64, f64, f64) + Send + 'static,
|
||||
) -> f64 {
|
||||
create_callback(CallbackHandler::Callback7(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_8(
|
||||
cb: impl FnMut(f64, f64, f64, f64, f64, f64, f64, f64) + Send + 'static,
|
||||
) -> f64 {
|
||||
create_callback(CallbackHandler::Callback8(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_9(
|
||||
cb: impl FnMut(f64, f64, f64, f64, f64, f64, f64, f64, f64) + Send + 'static,
|
||||
) -> f64 {
|
||||
create_callback(CallbackHandler::Callback9(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub fn create_callback_10(
|
||||
cb: impl FnMut(f64, f64, f64, f64, f64, f64, f64, f64, f64, f64) + Send + 'static,
|
||||
) -> f64 {
|
||||
create_callback(CallbackHandler::Callback10(Box::new(cb)))
|
||||
}
|
||||
|
||||
pub struct CallbackFuture {
|
||||
shared_state: Arc<Mutex<SharedState>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: Option<f64>,
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture {
|
||||
type Output = Option<f64>;
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture {
|
||||
pub fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: None,
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback1(Box::new(move |v: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = Some(v);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
})));
|
||||
(CallbackFuture { shared_state }, id as f64)
|
||||
}
|
||||
}
|
||||
|
||||
struct CallbackFuture0 {
|
||||
shared_state: Arc<Mutex<SharedState0>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState0 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture0 {
|
||||
type Output = ();
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture0 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState0 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback0(Box::new(move || {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = ();
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
})));
|
||||
(CallbackFuture0 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_0() -> (impl Future, f64) {
|
||||
CallbackFuture0::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture1 {
|
||||
shared_state: Arc<Mutex<SharedState1>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState1 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: f64,
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture1 {
|
||||
type Output = f64;
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture1 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState1 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: 0.0,
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback1(Box::new(move |v: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = v;
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
})));
|
||||
(CallbackFuture1 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_1() -> (impl Future, f64) {
|
||||
CallbackFuture1::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture2 {
|
||||
shared_state: Arc<Mutex<SharedState2>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState2 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture2 {
|
||||
type Output = (f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture2 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState2 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback2(Box::new(
|
||||
move |a1: f64, a2: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture2 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_2() -> (impl Future, f64) {
|
||||
CallbackFuture2::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture3 {
|
||||
shared_state: Arc<Mutex<SharedState3>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState3 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture3 {
|
||||
type Output = (f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture3 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState3 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback3(Box::new(
|
||||
move |a1: f64, a2: f64, a3: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture3 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_3() -> (impl Future, f64) {
|
||||
CallbackFuture3::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture4 {
|
||||
shared_state: Arc<Mutex<SharedState4>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState4 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture4 {
|
||||
type Output = (f64, f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture4 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState4 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback4(Box::new(
|
||||
move |a1: f64, a2: f64, a3: f64, a4: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3, a4);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture4 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_4() -> (impl Future, f64) {
|
||||
CallbackFuture4::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture5 {
|
||||
shared_state: Arc<Mutex<SharedState5>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState5 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture5 {
|
||||
type Output = (f64, f64, f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture5 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState5 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback5(Box::new(
|
||||
move |a1: f64, a2: f64, a3: f64, a4: f64, a5: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3, a4, a5);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture5 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_5() -> (impl Future, f64) {
|
||||
CallbackFuture5::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture6 {
|
||||
shared_state: Arc<Mutex<SharedState6>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState6 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture6 {
|
||||
type Output = (f64, f64, f64, f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture6 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState6 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback6(Box::new(
|
||||
move |a1: f64, a2: f64, a3: f64, a4: f64, a5: f64, a6: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3, a4, a5, a6);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture6 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_6() -> (impl Future, f64) {
|
||||
CallbackFuture6::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture7 {
|
||||
shared_state: Arc<Mutex<SharedState7>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState7 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64, f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture7 {
|
||||
type Output = (f64, f64, f64, f64, f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture7 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState7 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback7(Box::new(
|
||||
move |a1: f64, a2: f64, a3: f64, a4: f64, a5: f64, a6: f64, a7: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3, a4, a5, a6, a7);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture7 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_7() -> (impl Future, f64) {
|
||||
CallbackFuture7::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture8 {
|
||||
shared_state: Arc<Mutex<SharedState8>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState8 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64, f64, f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture8 {
|
||||
type Output = (f64, f64, f64, f64, f64, f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture8 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState8 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback8(Box::new(
|
||||
move |a1: f64, a2: f64, a3: f64, a4: f64, a5: f64, a6: f64, a7: f64, a8: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3, a4, a5, a6, a7, a8);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture8 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_8() -> (impl Future, f64) {
|
||||
CallbackFuture8::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture9 {
|
||||
shared_state: Arc<Mutex<SharedState9>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState9 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64, f64, f64, f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture9 {
|
||||
type Output = (f64, f64, f64, f64, f64, f64, f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture9 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState9 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback9(Box::new(
|
||||
move |a1: f64,
|
||||
a2: f64,
|
||||
a3: f64,
|
||||
a4: f64,
|
||||
a5: f64,
|
||||
a6: f64,
|
||||
a7: f64,
|
||||
a8: f64,
|
||||
a9: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture9 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_9() -> (impl Future, f64) {
|
||||
CallbackFuture9::new()
|
||||
}
|
||||
|
||||
struct CallbackFuture10 {
|
||||
shared_state: Arc<Mutex<SharedState10>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the waiting thread
|
||||
struct SharedState10 {
|
||||
completed: bool,
|
||||
waker: Option<Waker>,
|
||||
result: (f64, f64, f64, f64, f64, f64, f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Future for CallbackFuture10 {
|
||||
type Output = (f64, f64, f64, f64, f64, f64, f64, f64, f64, f64);
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(shared_state.result)
|
||||
} else {
|
||||
shared_state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFuture10 {
|
||||
fn new() -> (Self, f64) {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState10 {
|
||||
completed: false,
|
||||
waker: None,
|
||||
result: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
}));
|
||||
|
||||
let thread_shared_state = shared_state.clone();
|
||||
let id = create_callback(CallbackHandler::Callback10(Box::new(
|
||||
move |a1: f64,
|
||||
a2: f64,
|
||||
a3: f64,
|
||||
a4: f64,
|
||||
a5: f64,
|
||||
a6: f64,
|
||||
a7: f64,
|
||||
a8: f64,
|
||||
a9: f64,
|
||||
a10: f64| {
|
||||
let mut shared_state = thread_shared_state.lock();
|
||||
shared_state.completed = true;
|
||||
shared_state.result = (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
|
||||
if let Some(waker) = shared_state.waker.take() {
|
||||
core::mem::drop(shared_state);
|
||||
waker.wake()
|
||||
}
|
||||
},
|
||||
)));
|
||||
(CallbackFuture10 { shared_state }, id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_callback_future_10() -> (impl Future, f64) {
|
||||
CallbackFuture10::new()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn handle_callback(
|
||||
id: f64,
|
||||
a1: f64,
|
||||
a2: f64,
|
||||
a3: f64,
|
||||
a4: f64,
|
||||
a5: f64,
|
||||
a6: f64,
|
||||
a7: f64,
|
||||
a8: f64,
|
||||
a9: f64,
|
||||
a10: f64,
|
||||
) {
|
||||
let h = get_callback(id);
|
||||
let handler_ref = h.unwrap();
|
||||
let mut handler = handler_ref.lock();
|
||||
match &mut *handler {
|
||||
CallbackHandler::Callback0(c) => c(),
|
||||
CallbackHandler::Callback1(c) => c(a1),
|
||||
CallbackHandler::Callback2(c) => c(a1, a2),
|
||||
CallbackHandler::Callback3(c) => c(a1, a2, a3),
|
||||
CallbackHandler::Callback4(c) => c(a1, a2, a3, a4),
|
||||
CallbackHandler::Callback5(c) => c(a1, a2, a3, a4, a5),
|
||||
CallbackHandler::Callback6(c) => c(a1, a2, a3, a4, a5, a6),
|
||||
CallbackHandler::Callback7(c) => c(a1, a2, a3, a4, a5, a6, a7),
|
||||
CallbackHandler::Callback8(c) => c(a1, a2, a3, a4, a5, a6, a7, a8),
|
||||
CallbackHandler::Callback9(c) => c(a1, a2, a3, a4, a5, a6, a7, a8, a9),
|
||||
CallbackHandler::Callback10(c) => c(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "cargo-webassembly"
|
||||
version = "0.0.18"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A cargo utility for working on WebAssembly"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
url= "2.2.0"
|
||||
webbrowser = "0.5.5"
|
||||
clap = { version = "2.33.3", features = ["yaml"] }
|
||||
colored = "2.0.0"
|
||||
tide = "0.15.0"
|
||||
async-std = { version = "1.7.0", features = ["attributes"] }
|
||||
@@ -0,0 +1,78 @@
|
||||
# cargo-webassembly
|
||||
|
||||
This cargo extension is for developing front end applications in WebAssembly. Use it's subcommands to generate Rust projects ready to develop WebAssembly immediately, and use the tool to run a local dev server to see it while you work.
|
||||
|
||||
This project is currently **beta**, but totally functional! Next steps include:
|
||||
|
||||
* cleaning up the code to use less `unwrap`
|
||||
* use a better open url library (the current one has some weird behavior).
|
||||
* support project file watching and recompiling
|
||||
|
||||
<p align="center">
|
||||
<img height="300" src="../../images/undraw_website_builder_bxki.png">
|
||||
</p>
|
||||
|
||||
First make sure you [install Rust](https://rustup.rs/) and have the `wasm32` toolchain installed:
|
||||
|
||||
```
|
||||
rustup target add wasm32-unknown-unknown
|
||||
```
|
||||
|
||||
Install with the following command:
|
||||
|
||||
```
|
||||
cargo install cargo-webassembly
|
||||
```
|
||||
|
||||
# Create a new project
|
||||
|
||||
```
|
||||
cargo webassembly new my_project
|
||||
```
|
||||
|
||||
This will initialize a Rust WebAssembly project completely setup to run in the browser.
|
||||
|
||||
# Run your project
|
||||
|
||||
Go into your projects root directory (e.g. `cd my_project`)
|
||||
|
||||
```
|
||||
cargo webassembly run
|
||||
```
|
||||
|
||||
This will compile and start a server by default on port `8080`, you can change the port with the `-p` option.
|
||||
|
||||
This server is setup for single-page apps where all non-static file routes redirect to the root `index.html`.
|
||||
|
||||
# Building your project
|
||||
|
||||
```
|
||||
cargo webassembly build
|
||||
```
|
||||
|
||||
This command will just compile your WebAssembly and place everything you need for your web app in the `dist` folder.
|
||||
|
||||
# Next steps
|
||||
|
||||
Check out more ways to interact with the browser using the [`web`](https://docs.rs/web/) package!
|
||||
|
||||
Here's some cool examples:
|
||||
|
||||
* [snake](https://wasm.js.org/examples/snake/)
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `cargo-webassembly` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
@@ -0,0 +1,29 @@
|
||||
name: cargo-webassembly
|
||||
version: "0.0.12"
|
||||
author: Richard Anaya <richard.anaya@gmail.com>
|
||||
about: A tool for working with WebAssembly Rust projects
|
||||
subcommands:
|
||||
- webassembly:
|
||||
args:
|
||||
- version:
|
||||
short: V
|
||||
multiple: true
|
||||
help: Displays the version
|
||||
about: A collection of subcommands for working with WebAssembly
|
||||
subcommands:
|
||||
- new:
|
||||
about: creates new WebAssembly project
|
||||
args:
|
||||
- INPUT:
|
||||
help: Sets the name of the project directory to use
|
||||
required: true
|
||||
- init:
|
||||
about: creates new WebAssembly project in current directory
|
||||
- build:
|
||||
about: controls testing features
|
||||
- run:
|
||||
about: controls testing features
|
||||
args:
|
||||
- port:
|
||||
help: Sets the port to be used when running
|
||||
default_value: "8080"
|
||||
@@ -0,0 +1,213 @@
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
use clap::App;
|
||||
extern crate url;
|
||||
use colored::*;
|
||||
use std::env;
|
||||
use std::fs::create_dir;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn from_extension(extension: impl AsRef<str>) -> Option<tide::http::mime::Mime> {
|
||||
match extension.as_ref() {
|
||||
"html" => Some(tide::http::mime::HTML),
|
||||
"js" | "mjs" | "jsonp" => Some(tide::http::mime::JAVASCRIPT),
|
||||
"json" => Some(tide::http::mime::JSON),
|
||||
"css" => Some(tide::http::mime::CSS),
|
||||
"svg" => Some(tide::http::mime::SVG),
|
||||
"xml" => Some(tide::http::mime::XML),
|
||||
"png" => Some(tide::http::mime::PNG),
|
||||
"jpg" | "jpeg" => Some(tide::http::mime::JPEG),
|
||||
"wasm" => Some(tide::http::mime::WASM),
|
||||
"ico" => Some(tide::http::mime::ICO),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() -> tide::Result<()> {
|
||||
let yaml = load_yaml!("cli.yaml");
|
||||
let mut app = App::from_yaml(yaml);
|
||||
let matches = app.clone().get_matches();
|
||||
|
||||
let mut dir = env::current_dir().unwrap();
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("webassembly") {
|
||||
if let Some(matches) = matches.subcommand_matches("new") {
|
||||
dir = dir.join(matches.value_of("INPUT").unwrap());
|
||||
create_project_in_dir(&dir)
|
||||
} else if let Some(_) = matches.subcommand_matches("init") {
|
||||
create_project_in_dir(&dir)
|
||||
} else if let Some(_) = matches.subcommand_matches("build") {
|
||||
build_project_in_dir(&dir)
|
||||
} else if let Some(matches) = matches.subcommand_matches("run") {
|
||||
build_project_in_dir(&dir);
|
||||
let name = dir.file_name().unwrap().to_str().unwrap();
|
||||
let server_dir = dir.join("dist");
|
||||
let mut app = tide::new();
|
||||
let server_dir2 = server_dir.clone();
|
||||
app.at("/").get(move |_req: tide::Request<()>| {
|
||||
let index = server_dir.join("index.html");
|
||||
async move {
|
||||
tide::Result::Ok(
|
||||
tide::Response::builder(200)
|
||||
.body(std::fs::read(index).unwrap())
|
||||
.content_type(tide::http::mime::HTML)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
});
|
||||
app.at("/*").get(move |req: tide::Request<()>| {
|
||||
let server_dir3 = server_dir2.clone();
|
||||
async move {
|
||||
let index = server_dir3.join("index.html");
|
||||
let p = server_dir3.to_str().unwrap();
|
||||
let p2 = req.url().path();
|
||||
let s = format!("{}{}", p, p2).to_string();
|
||||
let p3 = std::path::Path::new(&s);
|
||||
if p3.exists() {
|
||||
tide::Result::Ok(
|
||||
tide::Response::builder(200)
|
||||
.body(std::fs::read(p3).unwrap())
|
||||
.content_type(
|
||||
from_extension(p3.extension().unwrap().to_str().unwrap())
|
||||
.unwrap(),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
} else {
|
||||
tide::Result::Ok(
|
||||
tide::Response::builder(200)
|
||||
.body(std::fs::read(index).unwrap())
|
||||
.content_type(tide::http::mime::HTML)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
let port = matches.value_of("port").unwrap().parse::<u32>().unwrap();
|
||||
let addr = format!("{}{}", "http://127.0.0.1:", port);
|
||||
let addr2 = addr.clone();
|
||||
async_std::task::spawn(async move { webbrowser::open(&addr2) });
|
||||
println!(
|
||||
" {} webassembly `{}` package on port {}",
|
||||
"Running".green().bold(),
|
||||
name,
|
||||
addr
|
||||
);
|
||||
app.listen(addr).await?;
|
||||
} else {
|
||||
if matches.is_present("version") {
|
||||
println!("{}", env!("CARGO_PKG_VERSION"))
|
||||
} else {
|
||||
app.print_long_help().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_project_in_dir(dir: &PathBuf) {
|
||||
let name = dir.file_name().unwrap().to_str().unwrap();
|
||||
if !dir.exists() {
|
||||
create_dir(dir).unwrap();
|
||||
}
|
||||
create_dir(dir.join("src")).unwrap();
|
||||
create_dir(dir.join("dist")).unwrap();
|
||||
std::fs::write(
|
||||
dir.join("Cargo.toml"),
|
||||
include_str!("template/Project.toml").replace("PROJECT", name),
|
||||
)
|
||||
.expect("Failed to write");
|
||||
std::fs::write(
|
||||
dir.join("src/lib.rs"),
|
||||
include_str!("template/lib.rs").replace("PROJECT", name),
|
||||
)
|
||||
.expect("Failed to write");
|
||||
std::fs::write(
|
||||
dir.join("dist/index.html"),
|
||||
include_str!("template/index.html").replace("PROJECT", name),
|
||||
)
|
||||
.expect("Failed to write");
|
||||
std::fs::write(
|
||||
dir.join("dist/js-wasm.js"),
|
||||
include_str!("template/js-wasm.js").replace("PROJECT", name),
|
||||
)
|
||||
.expect("Failed to write");
|
||||
println!(
|
||||
" {} webassembly `{}` package",
|
||||
"Created".green().bold(),
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
fn build_project_in_dir(dir: &PathBuf) {
|
||||
use std::io::{self, Write};
|
||||
use std::process::Command;
|
||||
|
||||
if !dir.join("Cargo.toml").exists() {
|
||||
println!("must execute this command in project root");
|
||||
return;
|
||||
}
|
||||
|
||||
let name = dir.file_name().unwrap().to_str().unwrap();
|
||||
println!(
|
||||
" {} webassembly `{}` package",
|
||||
"Pre-compile check".green().bold(),
|
||||
name
|
||||
);
|
||||
let mut target_check = Command::new("cargo");
|
||||
target_check
|
||||
.arg("check")
|
||||
.arg("--target")
|
||||
.arg("wasm32-unknown-unknown");
|
||||
let command_output = target_check
|
||||
.output()
|
||||
.expect("Build pre-check failed! (check that wasm32 build target is installed)");
|
||||
io::stdout().write_all(&command_output.stdout).unwrap();
|
||||
io::stderr().write_all(&command_output.stderr).unwrap();
|
||||
println!(
|
||||
" Pre-compile check exit code status: {}",
|
||||
command_output.status
|
||||
);
|
||||
|
||||
let name = dir.file_name().unwrap().to_str().unwrap();
|
||||
println!(
|
||||
" {} webassembly `{}` package",
|
||||
"Compiling".green().bold(),
|
||||
name
|
||||
);
|
||||
|
||||
println!(
|
||||
" {} webassembly `{}` package",
|
||||
"Compiling".green().bold(),
|
||||
name
|
||||
);
|
||||
let mut echo_hello = Command::new("cargo");
|
||||
echo_hello
|
||||
.arg("build")
|
||||
.arg("--target")
|
||||
.arg("wasm32-unknown-unknown")
|
||||
.arg("--release");
|
||||
let compile_command_output = echo_hello.output().expect("Could not compile to wasm");
|
||||
|
||||
io::stdout()
|
||||
.write_all(&compile_command_output.stdout)
|
||||
.unwrap();
|
||||
io::stderr()
|
||||
.write_all(&compile_command_output.stderr)
|
||||
.unwrap();
|
||||
println!(
|
||||
" Compilation exit code status: {}",
|
||||
compile_command_output.status
|
||||
);
|
||||
|
||||
std::fs::copy(
|
||||
dir.join(format!(
|
||||
"target/wasm32-unknown-unknown/release/{}.wasm",
|
||||
name.replace("-", "_")
|
||||
)),
|
||||
dir.join(format!("dist/{}.wasm", name)),
|
||||
)
|
||||
.expect("Could not copy built file! (check that wasm32 build target is installed)");
|
||||
println!(" {} webassembly target", "Finished".green().bold());
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "PROJECT"
|
||||
version = "0.0.0"
|
||||
authors = [""]
|
||||
edition = "2018"
|
||||
description = ""
|
||||
categories = ["wasm"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
web = "0"
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>PROJECT</title>
|
||||
<link rel="shortcut icon" href="#" />
|
||||
<script src="/js-wasm.js"></script>
|
||||
<!-- If you don't feel like hosting js-wasm yourself you can also use the CDN url -->
|
||||
<!-- https://unpkg.com/js-wasm/js-wasm.js -->
|
||||
<script type="application/wasm" src="/PROJECT.wasm"></script>
|
||||
</head>
|
||||
</html>
|
||||
@@ -0,0 +1,304 @@
|
||||
class Index {
|
||||
constructor(index, generation) {
|
||||
this.index = index;
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
toNum() {
|
||||
return Number(BigInt(this.generation) << BigInt(32) | BigInt(this.index));
|
||||
}
|
||||
}
|
||||
|
||||
Index.fromNum = function (n) {
|
||||
let i = Number(((BigInt(n) & BigInt(0xffffffff00000000)) >> BigInt(32)) & BigInt(0xffffffff));
|
||||
let g = n & 0xffffffff;
|
||||
return new Index(g, i);
|
||||
};
|
||||
|
||||
class GenerationalArena {
|
||||
constructor() {
|
||||
this.items = [];
|
||||
this.generation = 0;
|
||||
this.free_list_head = null;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
insert(v) {
|
||||
// lets use the first free entry if we have one
|
||||
if (this.free_list_head !== null) {
|
||||
let i = this.free_list_head;
|
||||
this.free_list_head = this.items[i].next_free;
|
||||
this.items[i] = {
|
||||
generation: this.generation,
|
||||
value: v
|
||||
};
|
||||
this.length += 1;
|
||||
return new Index(i, this.generation);
|
||||
}
|
||||
|
||||
this.items.push({
|
||||
generation: this.generation,
|
||||
value: v
|
||||
});
|
||||
let idx = new Index(this.items.length - 1, this.generation);
|
||||
this.length += 1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
contains(idx) {
|
||||
return this.get(idx) !== undefined;
|
||||
}
|
||||
|
||||
get(i) {
|
||||
let e = this.items[i.index];
|
||||
if (e && e.generation === i.generation) {
|
||||
return e.value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
remove(idx) {
|
||||
if (idx.index >= this.items.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let e = this.items[idx.index];
|
||||
if (e.generation !== undefined && e.generation == idx.generation) {
|
||||
this.generation += 1;
|
||||
this.items[idx.index] = {
|
||||
next_free: this.free_list_head
|
||||
};
|
||||
this.free_list_head = idx.index;
|
||||
this.length -= 1;
|
||||
return e.value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
for (var i = 0; i < this.items.length; i++) {
|
||||
let x = this.items[i];
|
||||
if (x.generation !== undefined) {
|
||||
yield { index: new Index(i, x.generation), value: x.value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indices() {
|
||||
let i = { items: this.items };
|
||||
i[Symbol.iterator] = function* iter() {
|
||||
for (var i = 0; i < this.items.length; i++) {
|
||||
let x = this.items[i];
|
||||
if (x.generation !== undefined) {
|
||||
yield new Index(i, x.generation);
|
||||
}
|
||||
}
|
||||
};
|
||||
return i;
|
||||
}
|
||||
|
||||
values() {
|
||||
let i = { items: this.items };
|
||||
i[Symbol.iterator] = function* iter() {
|
||||
for (var i = 0; i < this.items.length; i++) {
|
||||
let x = this.items[i];
|
||||
if (x.generation !== undefined) {
|
||||
yield x.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
window.JsWasm = {
|
||||
createEnvironment() {
|
||||
let arena = new GenerationalArena();
|
||||
arena.insert(undefined);
|
||||
arena.insert(null);
|
||||
arena.insert(self);
|
||||
arena.insert(typeof document != "undefined" ? document : null);
|
||||
arena.insert(typeof document != "undefined" ? document.body : null);
|
||||
let context = {
|
||||
functions: [
|
||||
function () {
|
||||
debugger;
|
||||
}
|
||||
],
|
||||
objects: arena,
|
||||
utf8dec: new TextDecoder("utf-8"),
|
||||
utf8enc: new TextEncoder("utf-8"),
|
||||
utf16dec: new TextDecoder("utf-16"),
|
||||
utf16enc: new TextEncoder("utf-16"),
|
||||
toCallbackArg: function (arg) {
|
||||
if (typeof arg === "object") {
|
||||
return context.storeObject(arg);
|
||||
}
|
||||
return arg;
|
||||
},
|
||||
createCallback: function (cb) {
|
||||
let fnHandleCallback = this.module.instance.exports.handle_callback;
|
||||
return function () {
|
||||
const arg = arguments;
|
||||
fnHandleCallback(
|
||||
cb,
|
||||
context.toCallbackArg(arg[0]),
|
||||
context.toCallbackArg(arg[1]),
|
||||
context.toCallbackArg(arg[2]),
|
||||
context.toCallbackArg(arg[3]),
|
||||
context.toCallbackArg(arg[4]),
|
||||
context.toCallbackArg(arg[5]),
|
||||
context.toCallbackArg(arg[6]),
|
||||
context.toCallbackArg(arg[7]),
|
||||
context.toCallbackArg(arg[8]),
|
||||
context.toCallbackArg(arg[9])
|
||||
);
|
||||
};
|
||||
},
|
||||
readCStringFromMemory: function (start) {
|
||||
const data = new Uint8Array(this.module.instance.exports.memory.buffer);
|
||||
const str = [];
|
||||
let i = start;
|
||||
while (data[i] !== 0) {
|
||||
str.push(data[i]);
|
||||
i++;
|
||||
}
|
||||
return this.utf8dec.decode(new Uint8Array(str));
|
||||
},
|
||||
writeCStringToMemory(str) {
|
||||
const bytes = this.utf8enc.encode(str + String.fromCharCode(0));
|
||||
const len = bytes.length;
|
||||
const start = this.module.instance.exports.malloc(len);
|
||||
const memory = new Uint8Array(
|
||||
this.module.instance.exports.memory.buffer
|
||||
);
|
||||
memory.set(bytes, start);
|
||||
return start;
|
||||
},
|
||||
readUtf8FromMemory: function (start, len) {
|
||||
const memory = new Uint8Array(
|
||||
this.module.instance.exports.memory.buffer
|
||||
);
|
||||
const text = this.utf8dec.decode(memory.subarray(start, start + len));
|
||||
return text;
|
||||
},
|
||||
writeUtf8ToMemory: function (str) {
|
||||
const bytes = utf8enc.encode(str);
|
||||
const len = bytes.length;
|
||||
const start = this.module.instance.exports.malloc(len);
|
||||
const memory = new Uint8Array(
|
||||
this.module.instance.exports.memory.buffer
|
||||
);
|
||||
memory.set(bytes, start);
|
||||
return start;
|
||||
},
|
||||
readUtf16FromMemory: function (start, len) {
|
||||
const memory = new Uint8Array(
|
||||
this.module.instance.exports.memory.buffer
|
||||
);
|
||||
const text = this.utf16dec.decode(memory.subarray(start, start + len));
|
||||
return text;
|
||||
},
|
||||
writeUtf16ToMemory: function (str) {
|
||||
const bytes = utf16enc.encode(str);
|
||||
const len = bytes.length;
|
||||
const start = this.module.instance.exports.malloc(len);
|
||||
const memory = new Uint8Array(
|
||||
this.module.instance.exports.memory.buffer
|
||||
);
|
||||
memory.set(bytes, start);
|
||||
return start;
|
||||
},
|
||||
readUint8ArrayFromMemory(start) {
|
||||
const data32 = new Uint32Array(
|
||||
this.module.instance.exports.memory.buffer
|
||||
);
|
||||
const ptr = data32[start / 4];
|
||||
const length = data32[ptr / 4];
|
||||
let b = mem.slice(ptr + 4, ptr + 4 + length);
|
||||
return new Uint8Array(b);
|
||||
},
|
||||
storeObject: function (obj) {
|
||||
const index = this.objects.insert(obj);
|
||||
return index.toNum();
|
||||
},
|
||||
getObject: function (handle) {
|
||||
return this.objects.get(Index.fromNum(handle));
|
||||
},
|
||||
releaseObject: function (handle) {
|
||||
this.objects.remove(Index.fromNum(handle));
|
||||
}
|
||||
};
|
||||
return {
|
||||
context,
|
||||
abort() {
|
||||
throw new Error("WebAssembly module aborted");
|
||||
},
|
||||
js_release(obj) {
|
||||
context.releaseObject(obj);
|
||||
},
|
||||
js_register_function(start, len, utfByteLen) {
|
||||
let functionBody;
|
||||
if (utfByteLen === 16) {
|
||||
functionBody = context.readUtf16FromMemory(start, len);
|
||||
} else {
|
||||
functionBody = context.readUtf8FromMemory(start, len);
|
||||
}
|
||||
let id = context.functions.length;
|
||||
context.functions.push(
|
||||
Function(`"use strict";return(${functionBody})`)()
|
||||
);
|
||||
return id;
|
||||
},
|
||||
js_invoke_function(funcHandle, a, b, c, d, e, f, g, h, i, j) {
|
||||
return context.functions[funcHandle].call(
|
||||
context,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
g,
|
||||
h,
|
||||
i,
|
||||
j
|
||||
);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
async load_and_run_wasm(wasmURL) {
|
||||
const env = JsWasm.createEnvironment();
|
||||
const response = await fetch(wasmURL);
|
||||
const bytes = await response.arrayBuffer();
|
||||
const module = await WebAssembly.instantiate(bytes, {
|
||||
env
|
||||
});
|
||||
env.context.module = module;
|
||||
module.instance.exports.main();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const wasmScripts = document.querySelectorAll(
|
||||
"script[type='application/wasm']"
|
||||
);
|
||||
for (let i = 0; i < wasmScripts.length; i++) {
|
||||
const src = wasmScripts[i].src;
|
||||
if (src) {
|
||||
JsWasm.load_and_run_wasm(src);
|
||||
} else {
|
||||
console.error("Script tag must have 'src' property.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (window.WasmScriptComponents) {
|
||||
window.WasmScriptComponents["js-wasm"] = function (e) {
|
||||
return {
|
||||
...e,
|
||||
...JsWasm.createEnvironment()
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use web::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() {
|
||||
set_inner_html(DOM_BODY,"Hello World!");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "class_names"
|
||||
version = "0.0.2"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A macro library for elegantly joining class names for web frameworks"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
@@ -0,0 +1,46 @@
|
||||
# class_names
|
||||
|
||||
<a href="https://docs.rs/class_names"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
class_names="0"
|
||||
```
|
||||
|
||||
# How to use
|
||||
|
||||
This library includes a macro for easily expressing a list of CSS class names (some which may be optional). The macro takes in a mixed list of `&str`,`String`,`Option<&str>`, or `Option<String>` and calculates a final list of class names for the HTML `class` attribute. Here's some examples
|
||||
|
||||
1. use strings
|
||||
```rust
|
||||
class_names!("big-button", "red".to_string()) // "big-button red"
|
||||
```
|
||||
2. accepts optionals
|
||||
```rust
|
||||
class_names!("big-button", if btn_red { Some("red") } else { None } )
|
||||
```
|
||||
3. a concise way of writing an optional
|
||||
```rust
|
||||
class_names!("big-button", btn_inactive.then(|| "inactive"))
|
||||
```
|
||||
4. one day from future features in [Rust nightly](https://doc.rust-lang.org/std/primitive.bool.html#method.then_some) you'll be able to write like this
|
||||
```rust
|
||||
class_names!("big-button", btn_inactive.then_some("inactive"))
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `class_names` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
@@ -0,0 +1,75 @@
|
||||
#![no_std]
|
||||
use core::cell::RefCell;
|
||||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub trait AddClassList<'a> {
|
||||
fn insert_into_list(&'a self, list: &'a ClassList<'a>);
|
||||
}
|
||||
|
||||
impl<'a> AddClassList<'a> for &str {
|
||||
fn insert_into_list(&'a self, list: &'a ClassList<'a>) {
|
||||
list.classes.borrow_mut().push(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddClassList<'a> for Option<&str> {
|
||||
fn insert_into_list(&'a self, list: &'a ClassList<'a>) {
|
||||
match self {
|
||||
Some(t) => list.classes.borrow_mut().push(t),
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddClassList<'a> for Option<String> {
|
||||
fn insert_into_list(&'a self, list: &'a ClassList<'a>) {
|
||||
match self {
|
||||
Some(t) => list.classes.borrow_mut().push(&t),
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddClassList<'a> for String {
|
||||
fn insert_into_list(&'a self, list: &'a ClassList<'a>) {
|
||||
list.classes.borrow_mut().push(&self);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClassList<'a> {
|
||||
classes: RefCell<Vec<&'a str>>,
|
||||
}
|
||||
|
||||
impl<'a> ClassList<'a> {
|
||||
pub fn new() -> ClassList<'a> {
|
||||
ClassList {
|
||||
classes: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
pub fn add<T>(&'a self, item: &'a T)
|
||||
where
|
||||
T: AddClassList<'a>,
|
||||
{
|
||||
(&item).insert_into_list(self);
|
||||
}
|
||||
pub fn to_string(&self) -> String {
|
||||
self.classes.borrow_mut().join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! class_names {
|
||||
// `()` indicates that the macro takes no argument.
|
||||
($($element:expr),*) => {
|
||||
{
|
||||
let class_list = ClassList::new();
|
||||
$(
|
||||
let e = $element;
|
||||
class_list.add(&e);
|
||||
)*
|
||||
class_list.to_string()
|
||||
}
|
||||
};
|
||||
}
|
||||
16
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/Cargo.toml
Normal file
16
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "cstring"
|
||||
version = "0.1.1"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A super simple cstring library for WebAssembly"
|
||||
categories = ["wasm", "no-std"]
|
||||
|
||||
[dependencies]
|
||||
cty = "0.2.1"
|
||||
memchr = { version = "2.3.4", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["alloc"]
|
||||
alloc = []
|
||||
15
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/README.md
Normal file
15
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# cstring
|
||||
A super simple library for c strings in web assembly
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
cstring = "0.0.4"
|
||||
```
|
||||
|
||||
# javascript
|
||||
|
||||
I've also included a very useful es6 module for interacting with strings in web assembly memory
|
||||
|
||||
```javascript
|
||||
import { extractCString, insertString } from "https://cdn.jsdelivr.net/gh/richardanaya/cstring/cstring.js";
|
||||
```
|
||||
22
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/cstring.js
Normal file
22
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/cstring.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const utf8dec = new TextDecoder("utf-8");
|
||||
const utf8enc = new TextEncoder("utf-8");
|
||||
|
||||
export function extractCString(mem, ptr) {
|
||||
const memory = new Uint8Array(mem);
|
||||
const str = [];
|
||||
let i = ptr;
|
||||
while (memory[i] !== 0) {
|
||||
str.push(memory[i]);
|
||||
i++;
|
||||
}
|
||||
return utf8dec.decode(new Uint8Array(str));
|
||||
}
|
||||
|
||||
export function insertString(getMemory, malloc, str) {
|
||||
const bytes = utf8enc.encode(str);
|
||||
const len = bytes.length;
|
||||
const start = malloc(len);
|
||||
const memory = new Uint8Array(getMemory());
|
||||
memory.set(bytes, start);
|
||||
return [start, len];
|
||||
}
|
||||
1377
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/src/cstr_core.rs
Normal file
1377
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/src/cstr_core.rs
Normal file
File diff suppressed because it is too large
Load Diff
24
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/src/lib.rs
Normal file
24
__wasm/js-wasm/helloworld/js-wasm/crates/cstring/src/lib.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
#![no_std]
|
||||
mod cstr_core;
|
||||
extern crate alloc;
|
||||
use crate::alloc::borrow::ToOwned;
|
||||
use alloc::string::String;
|
||||
use core::convert::TryInto;
|
||||
|
||||
pub fn from_str(s: &str) -> usize {
|
||||
cstr_core::CString::new(s).unwrap().into_raw() as usize
|
||||
}
|
||||
|
||||
pub fn try_into_string(start: impl TryInto<i32>) -> Result<String, &'static str> {
|
||||
if let Ok(pos) = start.try_into() {
|
||||
let s: &cstr_core::CStr =
|
||||
unsafe { cstr_core::CStr::from_ptr(pos as *const cstr_core::c_char) };
|
||||
if let Ok(s) = s.to_str() {
|
||||
Ok(s.to_owned())
|
||||
} else {
|
||||
Err("error creating cstring")
|
||||
}
|
||||
} else {
|
||||
Err("could not decypher cstring starting point")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "html_color"
|
||||
version = "0.0.0"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A library of web colors"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
150
__wasm/js-wasm/helloworld/js-wasm/crates/html_color/src/lib.rs
Normal file
150
__wasm/js-wasm/helloworld/js-wasm/crates/html_color/src/lib.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
#![no_std]
|
||||
|
||||
pub const ALICEBLUE: &'static str = "aliceblue";
|
||||
pub const ANTIQUEWHITE: &'static str = "antiquewhite";
|
||||
pub const AQUA: &'static str = "aqua";
|
||||
pub const AQUAMARINE: &'static str = "aquamarine";
|
||||
pub const AZURE: &'static str = "azure";
|
||||
pub const BEIGE: &'static str = "beige";
|
||||
pub const BISQUE: &'static str = "bisque";
|
||||
pub const BLACK: &'static str = "black";
|
||||
pub const BLANCHEDALMOND: &'static str = "blanchedalmond";
|
||||
pub const BLUE: &'static str = "blue";
|
||||
pub const BLUEVIOLET: &'static str = "blueviolet";
|
||||
pub const BROWN: &'static str = "brown";
|
||||
pub const BURLYWOOD: &'static str = "burlywood";
|
||||
pub const CADETBLUE: &'static str = "cadetblue";
|
||||
pub const CHARTREUSE: &'static str = "chartreuse";
|
||||
pub const CHOCOLATE: &'static str = "chocolate";
|
||||
pub const CORAL: &'static str = "coral";
|
||||
pub const CORNFLOWERBLUE: &'static str = "cornflowerblue";
|
||||
pub const CORNSILK: &'static str = "cornsilk";
|
||||
pub const CRIMSON: &'static str = "crimson";
|
||||
pub const CYAN: &'static str = "cyan";
|
||||
pub const DARKBLUE: &'static str = "darkblue";
|
||||
pub const DARKCYAN: &'static str = "darkcyan";
|
||||
pub const DARKGOLDENROD: &'static str = "darkgoldenrod";
|
||||
pub const DARKGRAY: &'static str = "darkgray";
|
||||
pub const DARKGREEN: &'static str = "darkgreen";
|
||||
pub const DARKGREY: &'static str = "darkgrey";
|
||||
pub const DARKKHAKI: &'static str = "darkkhaki";
|
||||
pub const DARKMAGENTA: &'static str = "darkmagenta";
|
||||
pub const DARKOLIVEGREEN: &'static str = "darkolivegreen";
|
||||
pub const DARKORANGE: &'static str = "darkorange";
|
||||
pub const DARKORCHID: &'static str = "darkorchid";
|
||||
pub const DARKRED: &'static str = "darkred";
|
||||
pub const DARKSALMON: &'static str = "darksalmon";
|
||||
pub const DARKSEAGREEN: &'static str = "darkseagreen";
|
||||
pub const DARKSLATEBLUE: &'static str = "darkslateblue";
|
||||
pub const DARKSLATEGRAY: &'static str = "darkslategray";
|
||||
pub const DARKSLATEGREY: &'static str = "darkslategrey";
|
||||
pub const DARKTURQUOISE: &'static str = "darkturquoise";
|
||||
pub const DARKVIOLET: &'static str = "darkviolet";
|
||||
pub const DEEPPINK: &'static str = "deeppink";
|
||||
pub const DEEPSKYBLUE: &'static str = "deepskyblue";
|
||||
pub const DIMGRAY: &'static str = "dimgray";
|
||||
pub const DIMGREY: &'static str = "dimgrey";
|
||||
pub const DODGERBLUE: &'static str = "dodgerblue";
|
||||
pub const FIREBRICK: &'static str = "firebrick";
|
||||
pub const FLORALWHITE: &'static str = "floralwhite";
|
||||
pub const FORESTGREEN: &'static str = "forestgreen";
|
||||
pub const FUCHSIA: &'static str = "fuchsia";
|
||||
pub const GAINSBORO: &'static str = "gainsboro";
|
||||
pub const GHOSTWHITE: &'static str = "ghostwhite";
|
||||
pub const GOLDENROD: &'static str = "goldenrod";
|
||||
pub const GOLD: &'static str = "gold";
|
||||
pub const GRAY: &'static str = "gray";
|
||||
pub const GREEN: &'static str = "green";
|
||||
pub const GREENYELLOW: &'static str = "greenyellow";
|
||||
pub const GREY: &'static str = "grey";
|
||||
pub const HONEYDEW: &'static str = "honeydew";
|
||||
pub const HOTPINK: &'static str = "hotpink";
|
||||
pub const INDIANRED: &'static str = "indianred";
|
||||
pub const INDIGO: &'static str = "indigo";
|
||||
pub const IVORY: &'static str = "ivory";
|
||||
pub const KHAKI: &'static str = "khaki";
|
||||
pub const LAVENDERBLUSH: &'static str = "lavenderblush";
|
||||
pub const LAVENDER: &'static str = "lavender";
|
||||
pub const LAWNGREEN: &'static str = "lawngreen";
|
||||
pub const LEMONCHIFFON: &'static str = "lemonchiffon";
|
||||
pub const LIGHTBLUE: &'static str = "lightblue";
|
||||
pub const LIGHTCORAL: &'static str = "lightcoral";
|
||||
pub const LIGHTCYAN: &'static str = "lightcyan";
|
||||
pub const LIGHTGOLDENRODYELLOW: &'static str = "lightgoldenrodyellow";
|
||||
pub const LIGHTGRAY: &'static str = "lightgray";
|
||||
pub const LIGHTGREEN: &'static str = "lightgreen";
|
||||
pub const LIGHTGREY: &'static str = "lightgrey";
|
||||
pub const LIGHTPINK: &'static str = "lightpink";
|
||||
pub const LIGHTSALMON: &'static str = "lightsalmon";
|
||||
pub const LIGHTSEAGREEN: &'static str = "lightseagreen";
|
||||
pub const LIGHTSKYBLUE: &'static str = "lightskyblue";
|
||||
pub const LIGHTSLATEGRAY: &'static str = "lightslategray";
|
||||
pub const LIGHTSLATEGREY: &'static str = "lightslategrey";
|
||||
pub const LIGHTSTEELBLUE: &'static str = "lightsteelblue";
|
||||
pub const LIGHTYELLOW: &'static str = "lightyellow";
|
||||
pub const LIME: &'static str = "lime";
|
||||
pub const LIMEGREEN: &'static str = "limegreen";
|
||||
pub const LINEN: &'static str = "linen";
|
||||
pub const MAGENTA: &'static str = "magenta";
|
||||
pub const MAROON: &'static str = "maroon";
|
||||
pub const MEDIUMAQUAMARINE: &'static str = "mediumaquamarine";
|
||||
pub const MEDIUMBLUE: &'static str = "mediumblue";
|
||||
pub const MEDIUMORCHID: &'static str = "mediumorchid";
|
||||
pub const MEDIUMPURPLE: &'static str = "mediumpurple";
|
||||
pub const MEDIUMSEAGREEN: &'static str = "mediumseagreen";
|
||||
pub const MEDIUMSLATEBLUE: &'static str = "mediumslateblue";
|
||||
pub const MEDIUMSPRINGGREEN: &'static str = "mediumspringgreen";
|
||||
pub const MEDIUMTURQUOISE: &'static str = "mediumturquoise";
|
||||
pub const MEDIUMVIOLETRED: &'static str = "mediumvioletred";
|
||||
pub const MIDNIGHTBLUE: &'static str = "midnightblue";
|
||||
pub const MINTCREAM: &'static str = "mintcream";
|
||||
pub const MISTYROSE: &'static str = "mistyrose";
|
||||
pub const MOCCASIN: &'static str = "moccasin";
|
||||
pub const NAVAJOWHITE: &'static str = "navajowhite";
|
||||
pub const NAVY: &'static str = "navy";
|
||||
pub const OLDLACE: &'static str = "oldlace";
|
||||
pub const OLIVE: &'static str = "olive";
|
||||
pub const OLIVEDRAB: &'static str = "olivedrab";
|
||||
pub const ORANGE: &'static str = "orange";
|
||||
pub const ORANGERED: &'static str = "orangered";
|
||||
pub const ORCHID: &'static str = "orchid";
|
||||
pub const PALEGOLDENROD: &'static str = "palegoldenrod";
|
||||
pub const PALEGREEN: &'static str = "palegreen";
|
||||
pub const PALETURQUOISE: &'static str = "paleturquoise";
|
||||
pub const PALEVIOLETRED: &'static str = "palevioletred";
|
||||
pub const PAPAYAWHIP: &'static str = "papayawhip";
|
||||
pub const PEACHPUFF: &'static str = "peachpuff";
|
||||
pub const PERU: &'static str = "peru";
|
||||
pub const PINK: &'static str = "pink";
|
||||
pub const PLUM: &'static str = "plum";
|
||||
pub const POWDERBLUE: &'static str = "powderblue";
|
||||
pub const PURPLE: &'static str = "purple";
|
||||
pub const REBECCAPURPLE: &'static str = "rebeccapurple";
|
||||
pub const RED: &'static str = "red";
|
||||
pub const ROSYBROWN: &'static str = "rosybrown";
|
||||
pub const ROYALBLUE: &'static str = "royalblue";
|
||||
pub const SADDLEBROWN: &'static str = "saddlebrown";
|
||||
pub const SALMON: &'static str = "salmon";
|
||||
pub const SANDYBROWN: &'static str = "sandybrown";
|
||||
pub const SEAGREEN: &'static str = "seagreen";
|
||||
pub const SEASHELL: &'static str = "seashell";
|
||||
pub const SIENNA: &'static str = "sienna";
|
||||
pub const SILVER: &'static str = "silver";
|
||||
pub const SKYBLUE: &'static str = "skyblue";
|
||||
pub const SLATEBLUE: &'static str = "slateblue";
|
||||
pub const SLATEGRAY: &'static str = "slategray";
|
||||
pub const SLATEGREY: &'static str = "slategrey";
|
||||
pub const SNOW: &'static str = "snow";
|
||||
pub const SPRINGGREEN: &'static str = "springgreen";
|
||||
pub const STEELBLUE: &'static str = "steelblue";
|
||||
pub const TAN: &'static str = "tan";
|
||||
pub const TEAL: &'static str = "teal";
|
||||
pub const THISTLE: &'static str = "thistle";
|
||||
pub const TOMATO: &'static str = "tomato";
|
||||
pub const TURQUOISE: &'static str = "turquoise";
|
||||
pub const VIOLET: &'static str = "violet";
|
||||
pub const WHEAT: &'static str = "wheat";
|
||||
pub const WHITE: &'static str = "white";
|
||||
pub const WHITESMOKE: &'static str = "whitesmoke";
|
||||
pub const YELLOW: &'static str = "yellow";
|
||||
pub const YELLOWGREEN: &'static str = "yellowgreen";
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "js-bindgen"
|
||||
version = "0.0.6"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
description = "Generate bindings that call JavaScript from WebAssembly"
|
||||
license = "MIT OR Apache-2.0"
|
||||
categories = ["wasm"]
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme="README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tera = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
Inflector = "0.11.4"
|
||||
serde_yaml = "0"
|
||||
clap="2"
|
||||
162
__wasm/js-wasm/helloworld/js-wasm/crates/js-bindgen/README.md
Normal file
162
__wasm/js-wasm/helloworld/js-wasm/crates/js-bindgen/README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# js-bindgen
|
||||
|
||||
<p align="center">
|
||||
<img height="300" src="../../images/undraw_convert_2gjv.png">
|
||||
</p>
|
||||
|
||||
**The project is very ALPHA right now, but it is generating some very basic bindings for Rust and C right now**
|
||||
|
||||
Generate WebAssembly bindings to JavaSCript via [`js-wasm`](https://wasm.js.org) for various languages:
|
||||
|
||||
* Rust
|
||||
* C
|
||||
* AssemblyScript
|
||||
|
||||
```
|
||||
cargo install js-bindgen
|
||||
```
|
||||
|
||||
# Getting Started
|
||||
|
||||
This project is able to take JavaScript API descriptions in yaml like the one below:
|
||||
|
||||
```yaml
|
||||
Bindings to web console
|
||||
----
|
||||
- namespace: console
|
||||
functions:
|
||||
- name: clear
|
||||
- name: log
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: warn
|
||||
friendly_name: warning
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: error
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: time
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
- name: timeEnd
|
||||
parameters:
|
||||
- name: msg
|
||||
parameter_type: string
|
||||
|
||||
```
|
||||
|
||||
And turn them into code.
|
||||
|
||||
# Rust
|
||||
|
||||
```
|
||||
js-bindgen --lang rust console.yaml
|
||||
```
|
||||
|
||||
```rust
|
||||
#![no_std]
|
||||
|
||||
pub mod console {
|
||||
use js::*;
|
||||
|
||||
pub fn clear(){
|
||||
let func = js!(r###"function(){
|
||||
console.clear();
|
||||
}"###);
|
||||
func.invoke_0();
|
||||
}
|
||||
|
||||
pub fn log(msg: &str){
|
||||
let a0 = msg.as_ptr() as u32;
|
||||
let a1 = msg.len() as u32;
|
||||
let func = js!(r###"function(msgPtr,msgLen){
|
||||
console.log(this.readUtf8FromMemory(msgPtr,msgLen));
|
||||
}"###);
|
||||
func.invoke_2(a0, a1);
|
||||
}
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
# C
|
||||
|
||||
```
|
||||
js-bindgen --lang c console.yaml
|
||||
```
|
||||
|
||||
```C
|
||||
#include "js-wasm.h"
|
||||
|
||||
void console_clear(){
|
||||
static int fn;
|
||||
char *fn_code = "function(){ console.clear(); }";
|
||||
if(fn == 0){
|
||||
fn = js_register_function(fn_code,js_strlen(fn_code));
|
||||
}
|
||||
js_invoke_function_0(fn);
|
||||
}
|
||||
|
||||
void console_log(char * msg){
|
||||
static int fn;
|
||||
unsigned int a0 = (unsigned int)msg;
|
||||
unsigned int a1 = js_strlen(msg);
|
||||
char *fn_code = "function(msgPtr,msgLen){ console.log(this.readUtf8FromMemory(msgPtr,msgLen)); }";
|
||||
if(fn == 0){
|
||||
fn = js_register_function(fn_code,js_strlen(fn_code));
|
||||
}
|
||||
js_invoke_function_2(fn, a0, a1);
|
||||
}
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
# AssemblyScript
|
||||
|
||||
```
|
||||
js-bindgen --lang assemblyscript console.yaml
|
||||
```
|
||||
|
||||
```ts
|
||||
import * as jswasm from "./js-wasm"
|
||||
|
||||
let console_clear_fn:f64 = 0;
|
||||
export function console_clear() : void {
|
||||
if( console_clear_fn === 0) {
|
||||
const code = `function(){ console.clear(); }`;
|
||||
console_clear_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_0(console_clear_fn);
|
||||
}
|
||||
|
||||
let console_log_fn:f64 = 0;
|
||||
export function console_log(msg: string) : void {
|
||||
const a0: f64 = <f64>changetype<usize>(msg);
|
||||
const a1: f64 = msg.length*2;
|
||||
if( console_log_fn === 0) {
|
||||
const code = `function(msgPtr,msgLen){ console.log(this.readUtf16FromMemory(msgPtr,msgLen)); }`;
|
||||
console_log_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
jswasm.js_invoke_function_2(console_log_fn, a0, a1);
|
||||
}
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
# Custom Code
|
||||
|
||||
Sometimes you may want to create a binding to code that doesn't exist and still have the power to generate libraries for many targets
|
||||
|
||||
```yaml
|
||||
- namespace: unicorn
|
||||
functions:
|
||||
makeUnicorns:
|
||||
code: |
|
||||
function() {
|
||||
console.log("🦄🦄🦄")
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,96 @@
|
||||
use inflector::Inflector;
|
||||
use serde::*;
|
||||
use tera::*;
|
||||
use clap::{Arg, App };
|
||||
|
||||
fn main() {
|
||||
let matches = App::new("js-bindgen")
|
||||
.version("0.0")
|
||||
.author("Richard Anaya <richard.anaya@gmail.com>")
|
||||
.about("Creates js-wasm bindings for various languages")
|
||||
.arg(Arg::with_name("lang")
|
||||
.short("l")
|
||||
.long("language")
|
||||
.help("Sets a custom config file")
|
||||
.takes_value(true)
|
||||
.required(true))
|
||||
.arg(Arg::with_name("INPUT")
|
||||
.help("Sets the input file to use")
|
||||
.required(true)
|
||||
.index(1))
|
||||
.get_matches();
|
||||
|
||||
|
||||
let mut tera = Tera::default();
|
||||
tera.add_raw_template("rust/module.rs", include_str!("templates/rust/module.rs"))
|
||||
.unwrap();
|
||||
|
||||
tera.add_raw_template("c/header.h", include_str!("templates/c/header.h"))
|
||||
.unwrap();
|
||||
|
||||
tera.add_raw_template("assemblyscript/module.ts", include_str!("templates/assemblyscript/module.ts"))
|
||||
.unwrap();
|
||||
|
||||
let file = matches.value_of("INPUT").unwrap();
|
||||
let text = std::fs::read_to_string(file).unwrap();
|
||||
|
||||
let mut bindings: Vec<Binding> = serde_yaml::from_str(&text).unwrap();
|
||||
|
||||
for n in bindings.iter_mut() {
|
||||
if let Some(fs) = &mut n.functions {
|
||||
for f in fs.iter_mut() {
|
||||
if f.friendly_name.is_none() {
|
||||
f.friendly_name = Some(f.name.to_snake_case())
|
||||
}
|
||||
if let Some(ps) = &mut f.parameters {
|
||||
for p in ps.iter_mut() {
|
||||
if p.friendly_name.is_none() {
|
||||
p.friendly_name = Some(p.name.to_snake_case())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
f.parameters = Some(vec![]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.functions = Some(vec![]);
|
||||
}
|
||||
}
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("bindings", &bindings);
|
||||
|
||||
if let Some(l) = matches.value_of("lang") {
|
||||
let r = if l == "rust" {
|
||||
tera.render("rust/module.rs", &context).unwrap()
|
||||
} else if l == "assemblyscript" {
|
||||
tera.render("assemblyscript/module.ts", &context).unwrap()
|
||||
} else {
|
||||
tera.render("c/header.h", &context).unwrap()
|
||||
};
|
||||
println!("{}", r);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct JSParameter {
|
||||
name: String,
|
||||
friendly_name: Option<String>,
|
||||
parameter_type: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct JSFunction {
|
||||
name: String,
|
||||
friendly_name: Option<String>,
|
||||
parameters: Option<Vec<JSParameter>>,
|
||||
output: Option<String>,
|
||||
code: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Binding {
|
||||
namespace: Option<String>,
|
||||
class: Option<String>,
|
||||
functions: Option<Vec<JSFunction>>,
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import * as jswasm from "./js-wasm"
|
||||
|
||||
{%- for binding in bindings %}
|
||||
|
||||
{%- for function in binding.functions %}
|
||||
let {{binding.namespace}}_{% if function.friendly_name -%}
|
||||
{{function.friendly_name}}
|
||||
{%- else -%}
|
||||
{{function.name}}
|
||||
{%- endif -%}_fn:f64 = 0;
|
||||
export function {{binding.namespace}}_{% if function.friendly_name -%}
|
||||
{{function.friendly_name}}
|
||||
{%- else -%}
|
||||
{{function.name}}
|
||||
{%- endif -%}(
|
||||
{%- for param in function.parameters -%}{{param.friendly_name}}: {% if param.parameter_type == "string" -%}
|
||||
string
|
||||
{%- else -%}
|
||||
f64
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%}){% if function.output %} : f64{% else %} : void{% endif %} {
|
||||
{% set_global i = 0 %}
|
||||
{%- for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
const a{{i}}: f64 = <f64>changetype<usize>({{param.friendly_name}});
|
||||
{% set_global i = i + 1 -%}
|
||||
const a{{i}}: f64 = {{param.friendly_name}}.length*2;
|
||||
{% else %}
|
||||
const a{{i}}: f64 = {{param.friendly_name}};
|
||||
{% endif -%}
|
||||
{% set_global i = i + 1 %}
|
||||
{%- endfor -%}
|
||||
if( {{binding.namespace}}_{% if function.friendly_name -%}
|
||||
{{function.friendly_name}}
|
||||
{%- else -%}
|
||||
{{function.name}}
|
||||
{%- endif -%}_fn === 0) {
|
||||
const code = `function({% for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
{{param.name}}Ptr,{{param.name}}Len
|
||||
{%- else -%}
|
||||
{{param.name}}
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%}){ {% if function.output -%}return {% endif %}{% if function.output == "object" %} this.storeObject({% endif %}{{binding.namespace}}.{{function.name}}({% for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
this.readUtf16FromMemory({{param.name}}Ptr,{{param.name}}Len)
|
||||
{%- elif param.parameter_type == "object" -%}this.getObject({{param.name}})
|
||||
{%- else -%}{{param.name}}
|
||||
{%- endif -%}{%- if not loop.last -%}, {% endif -%}{%- endfor -%}){% if function.output == "object" %}){% endif %}; }`;
|
||||
{{binding.namespace}}_{% if function.friendly_name -%}
|
||||
{{function.friendly_name}}
|
||||
{%- else -%}
|
||||
{{function.name}}
|
||||
{%- endif -%}_fn = <f64>jswasm.js_register_function(<f64>changetype<usize>(code),<f64>code.length*2, 16);
|
||||
}
|
||||
{% if function.output -%}return {% endif %}jswasm.js_invoke_function_{{i}}({{binding.namespace}}_{% if function.friendly_name -%}
|
||||
{{function.friendly_name}}
|
||||
{%- else -%}
|
||||
{{function.name}}
|
||||
{%- endif -%}_fn{% if function.parameters | length > 0 %}, {% endif %}{% set i = 0 -%}
|
||||
{%- for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
a{{i}}, {% set i = i + 1 -%}
|
||||
a{{i}}
|
||||
{%- else -%}
|
||||
a{{i}}
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%});
|
||||
}
|
||||
{% endfor %}
|
||||
{%- endfor -%}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "js-wasm.h"
|
||||
|
||||
{%- for binding in bindings %}
|
||||
{%- for function in binding.functions %}
|
||||
|
||||
{% if function.output -%}double{% else %}void{% endif %} {{binding.namespace}}_{% if function.friendly_name -%}
|
||||
{{function.friendly_name}}
|
||||
{%- else -%}
|
||||
{{function.name}}
|
||||
{%- endif -%}(
|
||||
{%- for param in function.parameters -%} {% if param.parameter_type == "string" -%}
|
||||
char *
|
||||
{%- else -%}
|
||||
double
|
||||
{%- endif %} {{param.friendly_name}}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif %}
|
||||
{%- endfor -%}){
|
||||
static int fn;
|
||||
{% set_global i = 0 %}
|
||||
{%- for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
unsigned int a{{i}} = (unsigned int){{param.friendly_name}};
|
||||
{% set_global i = i + 1 -%}
|
||||
unsigned int a{{i}} = js_strlen({{param.friendly_name}});
|
||||
{% else %}
|
||||
double a{{i}} = {{param.friendly_name}};
|
||||
{% endif -%}
|
||||
{% set_global i = i + 1 %}
|
||||
{%- endfor -%}
|
||||
char *fn_code = "function({% for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
{{param.name}}Ptr,{{param.name}}Len
|
||||
{%- else -%}
|
||||
{{param.name}}
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%}){ {% if function.output -%}return {% endif %}{% if function.output == "object" %} this.storeObject({% endif %}{{binding.namespace}}.{{function.name}}({% for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
this.readUtf8FromMemory({{param.name}}Ptr,{{param.name}}Len)
|
||||
{%- elif param.parameter_type == "object" -%}this.getObject({{param.name}})
|
||||
{%- else -%}{{param.name}}
|
||||
{%- endif -%}{%- if not loop.last -%}, {% endif -%}{%- endfor -%}){% if function.output == "object" %}){% endif %}; }";
|
||||
if(fn == 0){
|
||||
fn = js_register_function(fn_code,js_strlen(fn_code));
|
||||
}
|
||||
{% if function.output -%}return {% endif %}js_invoke_function_{{i}}(fn{% if function.parameters | length > 0 %}, {% endif %}{% set i = 0 -%}
|
||||
{%- for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
a{{i}}, {% set i = i + 1 -%}
|
||||
a{{i}}
|
||||
{%- else -%}
|
||||
a{{i}}
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%});
|
||||
}
|
||||
{%- endfor %}
|
||||
{%- endfor -%}
|
||||
@@ -0,0 +1,68 @@
|
||||
#![no_std]
|
||||
|
||||
use js::*;
|
||||
|
||||
{%- for binding in bindings %}
|
||||
|
||||
{%- for function in binding.functions %}
|
||||
|
||||
pub fn {{binding.namespace}}_{% if function.friendly_name -%}
|
||||
{{function.friendly_name}}
|
||||
{%- else -%}
|
||||
{{function.name}}
|
||||
{%- endif -%}(
|
||||
{%- for param in function.parameters -%}{{param.friendly_name}}: {% if param.parameter_type == "string" -%}
|
||||
&str
|
||||
{%- else -%}
|
||||
impl Into<f64>
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%}){% if function.output %} -> f64{% endif %} {
|
||||
{% set_global i = 0 %}
|
||||
{%- for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
let a{{i}} = {{param.friendly_name}}.as_ptr() as u32;
|
||||
{% set_global i = i + 1 -%}
|
||||
let a{{i}} = {{param.friendly_name}}.len() as u32;
|
||||
{% else -%}
|
||||
let a{{i}} = {{param.friendly_name}}.into();
|
||||
{% endif -%}
|
||||
{% set_global i = i + 1 %}
|
||||
{%- endfor -%}
|
||||
let func = js!(r###"function({% for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
{{param.name}}Ptr,{{param.name}}Len
|
||||
{%- else -%}
|
||||
{{param.name}}
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%}){
|
||||
{% if function.output -%}return {% endif %}{% if function.output == "object" %} this.storeObject({% endif %}{{binding.namespace}}.{{function.name}}({% for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
this.readUtf8FromMemory({{param.name}}Ptr,{{param.name}}Len)
|
||||
{%- elif param.parameter_type == "object" -%}
|
||||
this.getObject({{param.name}})
|
||||
{%- else -%}
|
||||
{{param.name}}
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%}){% if function.output == "object" %}){% endif %};
|
||||
}"###);
|
||||
func.invoke_{{i}}(
|
||||
{%- set i = 0 -%}
|
||||
{%- for param in function.parameters -%}
|
||||
{%- if param.parameter_type == "string" -%}
|
||||
a{{i}}, {% set i = i + 1 -%}
|
||||
a{{i}}
|
||||
{%- else -%}
|
||||
a{{i}}
|
||||
{%- endif -%}
|
||||
{%- if not loop.last -%}
|
||||
, {% endif -%}
|
||||
{%- endfor -%}){%- if not function.output -%};{%- endif %}
|
||||
}
|
||||
{%- endfor %}
|
||||
{%- endfor -%}
|
||||
17
__wasm/js-wasm/helloworld/js-wasm/crates/js/Cargo.toml
Normal file
17
__wasm/js-wasm/helloworld/js-wasm/crates/js/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "js"
|
||||
version = "0.4.3"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2021"
|
||||
description = "Call JavaScript from WebAssembly"
|
||||
license = "MIT OR Apache-2.0"
|
||||
categories = ["wasm", "no-std"]
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
rust-version="1.62"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
callback= {path="../callback",version="0.5"}
|
||||
spin = "0.9.4"
|
||||
20
__wasm/js-wasm/helloworld/js-wasm/crates/js/README.md
Normal file
20
__wasm/js-wasm/helloworld/js-wasm/crates/js/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# js-rs
|
||||
|
||||
A library for creating and invoking JavaScript functions from Rust.
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in 'js' by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
447
__wasm/js-wasm/helloworld/js-wasm/crates/js/src/lib.rs
Normal file
447
__wasm/js-wasm/helloworld/js-wasm/crates/js/src/lib.rs
Normal file
@@ -0,0 +1,447 @@
|
||||
#![no_std]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
pub use callback::*;
|
||||
use spin::Mutex;
|
||||
|
||||
pub const JS_NULL: f64 = 0.0;
|
||||
pub const JS_UNDEFINED: f64 = 1.0;
|
||||
pub const DOM_SELF: f64 = 2.0;
|
||||
pub const DOM_WINDOW: f64 = 2.0;
|
||||
pub const DOM_DOCUMENT: f64 = 3.0;
|
||||
pub const DOM_BODY: f64 = 4.0;
|
||||
|
||||
extern "C" {
|
||||
fn js_register_function(start: f64, len: f64) -> f64;
|
||||
fn js_release(obj: f64);
|
||||
fn js_invoke_function(
|
||||
fn_handle: f64,
|
||||
a: f64,
|
||||
b: f64,
|
||||
c: f64,
|
||||
d: f64,
|
||||
e: f64,
|
||||
f: f64,
|
||||
g: f64,
|
||||
h: f64,
|
||||
i: f64,
|
||||
j: f64,
|
||||
) -> f64;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct JSFunction {
|
||||
pub fn_handle: f64,
|
||||
}
|
||||
|
||||
impl From<f64> for JSFunction {
|
||||
fn from(f: f64) -> Self {
|
||||
JSFunction { fn_handle: f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&JSFunction> for f64 {
|
||||
fn from(f: &JSFunction) -> Self {
|
||||
f.fn_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl JSFunction {
|
||||
pub fn invoke_0(&self) -> f64
|
||||
where {
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_1<A>(&self, a: A) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_2<A, B>(&self, a: A, b: B) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_3<A, B, C>(&self, a: A, b: B, c: C) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_4<A, B, C, D>(&self, a: A, b: B, c: C, d: D) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
D: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
d.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_5<A, B, C, D, E>(&self, a: A, b: B, c: C, d: D, e: E) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
D: Into<f64>,
|
||||
E: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
d.into(),
|
||||
e.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_6<A, B, C, D, E, F>(&self, a: A, b: B, c: C, d: D, e: E, f: F) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
D: Into<f64>,
|
||||
E: Into<f64>,
|
||||
F: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
d.into(),
|
||||
e.into(),
|
||||
f.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_7<A, B, C, D, E, F, G>(&self, a: A, b: B, c: C, d: D, e: E, f: F, g: G) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
D: Into<f64>,
|
||||
E: Into<f64>,
|
||||
F: Into<f64>,
|
||||
G: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
d.into(),
|
||||
e.into(),
|
||||
f.into(),
|
||||
g.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_8<A, B, C, D, E, F, G, H>(
|
||||
&self,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
g: G,
|
||||
h: H,
|
||||
) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
D: Into<f64>,
|
||||
E: Into<f64>,
|
||||
F: Into<f64>,
|
||||
G: Into<f64>,
|
||||
H: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
d.into(),
|
||||
e.into(),
|
||||
f.into(),
|
||||
g.into(),
|
||||
h.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_9<A, B, C, D, E, F, G, H, I>(
|
||||
&self,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
g: G,
|
||||
h: H,
|
||||
i: I,
|
||||
) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
D: Into<f64>,
|
||||
E: Into<f64>,
|
||||
F: Into<f64>,
|
||||
G: Into<f64>,
|
||||
H: Into<f64>,
|
||||
I: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
d.into(),
|
||||
e.into(),
|
||||
f.into(),
|
||||
g.into(),
|
||||
h.into(),
|
||||
i.into(),
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke_10<A, B, C, D, E, F, G, H, I, J>(
|
||||
&self,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
g: G,
|
||||
h: H,
|
||||
i: I,
|
||||
j: J,
|
||||
) -> f64
|
||||
where
|
||||
A: Into<f64>,
|
||||
B: Into<f64>,
|
||||
C: Into<f64>,
|
||||
D: Into<f64>,
|
||||
E: Into<f64>,
|
||||
F: Into<f64>,
|
||||
G: Into<f64>,
|
||||
H: Into<f64>,
|
||||
I: Into<f64>,
|
||||
J: Into<f64>,
|
||||
{
|
||||
unsafe {
|
||||
js_invoke_function(
|
||||
self.fn_handle,
|
||||
a.into(),
|
||||
b.into(),
|
||||
c.into(),
|
||||
d.into(),
|
||||
e.into(),
|
||||
f.into(),
|
||||
g.into(),
|
||||
h.into(),
|
||||
i.into(),
|
||||
j.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_function(code: &str) -> JSFunction {
|
||||
let start = code.as_ptr();
|
||||
let len = code.len();
|
||||
unsafe {
|
||||
JSFunction {
|
||||
fn_handle: js_register_function(start as usize as f64, len as f64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JSObject {
|
||||
pub handle: f64,
|
||||
}
|
||||
|
||||
impl From<&JSObject> for f64 {
|
||||
fn from(f: &JSObject) -> Self {
|
||||
f.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for JSObject {
|
||||
fn from(n: f64) -> Self {
|
||||
JSObject { handle: n }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for JSObject {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
js_release(self.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ALLOCATIONS: Mutex<Vec<Option<Vec<u8>>>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn extract_string_from_memory(allocation_id: usize) -> String {
|
||||
let allocations = ALLOCATIONS.lock();
|
||||
let allocation = allocations.get(allocation_id).unwrap();
|
||||
let vec = allocation.as_ref().unwrap();
|
||||
String::from_utf8(vec.clone()).unwrap()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn create_allocation(size: usize) -> usize {
|
||||
let mut buf = Vec::with_capacity(size as usize);
|
||||
buf.resize(size, 0);
|
||||
let mut allocations = ALLOCATIONS.lock();
|
||||
let i = allocations.len();
|
||||
allocations.push(Some(buf));
|
||||
i
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn allocation_ptr(allocation_id: i32) -> *const u8 {
|
||||
let allocations = ALLOCATIONS.lock();
|
||||
let allocation = allocations.get(allocation_id as usize).unwrap();
|
||||
let vec = allocation.as_ref().unwrap();
|
||||
vec.as_ptr()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn allocation_len(allocation_id: i32) -> f64 {
|
||||
let allocations = ALLOCATIONS.lock();
|
||||
let allocation = allocations.get(allocation_id as usize).unwrap();
|
||||
let vec = allocation.as_ref().unwrap();
|
||||
vec.len() as f64
|
||||
}
|
||||
|
||||
pub fn clear_allocation(allocation_id: usize) {
|
||||
let mut allocations = ALLOCATIONS.lock();
|
||||
allocations[allocation_id] = None;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! js {
|
||||
($e:expr) => {{
|
||||
static mut FN: Option<f64> = None;
|
||||
unsafe {
|
||||
if FN.is_none() {
|
||||
FN = Some(js::register_function($e).fn_handle);
|
||||
}
|
||||
JSFunction {
|
||||
fn_handle: FN.unwrap(),
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
24
__wasm/js-wasm/helloworld/js-wasm/crates/web/Cargo.toml
Normal file
24
__wasm/js-wasm/helloworld/js-wasm/crates/web/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "web"
|
||||
version = "0.1.16"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A library for interacting with the web browser"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
web_common={path="../../crates/web_common",version="0"}
|
||||
web-dom={path="../../crates/web_dom",version="0"}
|
||||
web_console={path="../../crates/web_console",version="0"}
|
||||
web_random={path="../../crates/web_random",version="0"}
|
||||
web_timer={path="../../crates/web_timer",version="0"}
|
||||
web_canvas={path="../../crates/web_canvas",version="0"}
|
||||
html_color={path="../../crates/html_color",version="0"}
|
||||
js = {path="../js",version="0"}
|
||||
class_names= "0"
|
||||
spin = "0.9.0"
|
||||
web_local_storage={path="../../crates/web_local_storage",version="0"}
|
||||
70
__wasm/js-wasm/helloworld/js-wasm/crates/web/README.md
Normal file
70
__wasm/js-wasm/helloworld/js-wasm/crates/web/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# web-rs
|
||||
|
||||
<a href="https://docs.rs/web"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
A Rust library full of useful functions from various microlibraries for interacting with the web browser using [`js-wasm`](../../).
|
||||
|
||||
<p align="center">
|
||||
<img height="300" src="../../images/undraw_web_developer_p3e5.png">
|
||||
</p>
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
web = "0.1"
|
||||
```
|
||||
|
||||
```rust
|
||||
use web::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() {
|
||||
set_interval(|| {
|
||||
log(&format!("⏰ {}", random()));
|
||||
}, 1000);
|
||||
}
|
||||
```
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://unpkg.com/js-wasm/js-wasm.js"></script>
|
||||
<script type="application/wasm" src="helloworld.wasm"></script>
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
```make
|
||||
# cli commands for building web assembly
|
||||
build:
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/helloworld.wasm .
|
||||
lint:
|
||||
@cargo fmt
|
||||
serve:
|
||||
python3 -m http.server 8080
|
||||
```
|
||||
|
||||
- [x] console, errors, timing
|
||||
- [x] timers, render loops, intervals
|
||||
- [x] random numbers
|
||||
- [ ] DOM
|
||||
- [ ] canvas
|
||||
- [ ] webgl
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `web` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
12
__wasm/js-wasm/helloworld/js-wasm/crates/web/src/lib.rs
Normal file
12
__wasm/js-wasm/helloworld/js-wasm/crates/web/src/lib.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![no_std]
|
||||
pub use class_names::*;
|
||||
pub use html_color::*;
|
||||
pub use js::*;
|
||||
pub use spin::{Mutex, MutexGuard};
|
||||
pub use web_canvas::*;
|
||||
pub use web_common::*;
|
||||
pub use web_console::*;
|
||||
pub use web_dom::*;
|
||||
pub use web_local_storage::*;
|
||||
pub use web_random::*;
|
||||
pub use web_timer::*;
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "web_canvas"
|
||||
version = "0.0.7"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Web functions for canvas 2D"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
@@ -0,0 +1,20 @@
|
||||
# web_console
|
||||
|
||||
<a href="https://docs.rs/web_console"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in 'web_console' by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
@@ -0,0 +1,71 @@
|
||||
#![no_std]
|
||||
use js::*;
|
||||
|
||||
pub struct CanvasContext {
|
||||
pub handle: f64,
|
||||
}
|
||||
|
||||
pub trait Canvas2dApi {
|
||||
fn set_fill_color(&self, color: &str);
|
||||
fn fill_rect(&self, x: impl Into<f64>, y: impl Into<f64>, w: impl Into<f64>, h: impl Into<f64>);
|
||||
fn clear_rect(
|
||||
&self,
|
||||
x: impl Into<f64>,
|
||||
y: impl Into<f64>,
|
||||
w: impl Into<f64>,
|
||||
h: impl Into<f64>,
|
||||
);
|
||||
}
|
||||
|
||||
impl CanvasContext {
|
||||
pub fn from_canvas_element(el: impl Into<f64>) -> CanvasContext {
|
||||
CanvasContext {
|
||||
handle: {
|
||||
js!(r#"function(el){
|
||||
el = this.getObject(el);
|
||||
let ctx = el.getContext("2d");
|
||||
return this.storeObject(ctx);
|
||||
}"#)
|
||||
.invoke_1(el.into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Canvas2dApi for CanvasContext {
|
||||
fn set_fill_color(&self, color: &str) {
|
||||
js!("function(ctx,strPtr,strLen){
|
||||
ctx = this.getObject(ctx);
|
||||
ctx.fillStyle = this.readUtf8FromMemory(strPtr,strLen);
|
||||
}")
|
||||
.invoke_3(self.handle, color.as_ptr() as u32, color.len() as u32);
|
||||
}
|
||||
|
||||
fn fill_rect(
|
||||
&self,
|
||||
x: impl Into<f64>,
|
||||
y: impl Into<f64>,
|
||||
w: impl Into<f64>,
|
||||
h: impl Into<f64>,
|
||||
) {
|
||||
js!("function(ctx,x,y,w,h){
|
||||
ctx = this.getObject(ctx);
|
||||
ctx.fillRect(x,y,w,h);
|
||||
}")
|
||||
.invoke_5(self.handle, x, y, w, h);
|
||||
}
|
||||
|
||||
fn clear_rect(
|
||||
&self,
|
||||
x: impl Into<f64>,
|
||||
y: impl Into<f64>,
|
||||
w: impl Into<f64>,
|
||||
h: impl Into<f64>,
|
||||
) {
|
||||
js!("function(ctx,x,y,w,h){
|
||||
ctx = this.getObject(ctx);
|
||||
ctx.clearRect(x,y,w,h);
|
||||
}")
|
||||
.invoke_5(self.handle, x, y, w, h);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "web_common"
|
||||
version = "0.4.3"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Web functions for common operations"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
@@ -0,0 +1,20 @@
|
||||
# web_common
|
||||
|
||||
<a href="https://docs.rs/web_common"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `web_common` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
222
__wasm/js-wasm/helloworld/js-wasm/crates/web_common/src/lib.rs
Normal file
222
__wasm/js-wasm/helloworld/js-wasm/crates/web_common/src/lib.rs
Normal file
@@ -0,0 +1,222 @@
|
||||
#![no_std]
|
||||
use js::*;
|
||||
extern crate alloc;
|
||||
|
||||
pub trait GetProperty {
|
||||
fn get_property(el: impl Into<f64>, name: &str) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl GetProperty for f64 {
|
||||
fn get_property(el: impl Into<f64>, name: &str) -> Option<Self> {
|
||||
let v = el.into();
|
||||
if !is_property_number(v, name) {
|
||||
return None;
|
||||
}
|
||||
Some(
|
||||
js!("function(el,strPtr,strLen){
|
||||
el = this.getObject(el);
|
||||
return el[this.readUtf8FromMemory(strPtr,strLen)];
|
||||
}")
|
||||
.invoke_3(v, name.as_ptr() as u32, name.len() as u32),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetProperty for bool {
|
||||
fn get_property(el: impl Into<f64>, name: &str) -> Option<Self> {
|
||||
let v = js!(r#"function(el,strPtr,strLen){
|
||||
el = this.getObject(el);
|
||||
let i = el[this.readUtf8FromMemory(strPtr,strLen)];
|
||||
if(typeof i !== "boolean"){
|
||||
return -1;
|
||||
}
|
||||
return el[this.readUtf8FromMemory(strPtr,strLen)] ? 1 : 0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
if v == -1.0 {
|
||||
return None;
|
||||
} else {
|
||||
Some(v == 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetProperty for alloc::string::String {
|
||||
fn get_property(el: impl Into<f64>, name: &str) -> Option<Self> {
|
||||
let attr = js!(r#"function(o,strPtr,strLen){
|
||||
o = this.getObject(o);
|
||||
const a = o[this.readUtf8FromMemory(strPtr,strLen)];
|
||||
if(a === null){
|
||||
return -1;
|
||||
}
|
||||
return this.writeUtf8ToMemory(a);
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
if attr == -1.0 {
|
||||
return None;
|
||||
} else {
|
||||
let allocation_id = attr as usize;
|
||||
let s = extract_string_from_memory(allocation_id);
|
||||
clear_allocation(allocation_id);
|
||||
Some(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetProperty for JSObject {
|
||||
fn get_property(el: impl Into<f64>, name: &str) -> Option<Self> {
|
||||
let o = js!(r#"function(o,strPtr,strLen){
|
||||
o = this.getObject(o);
|
||||
const a = o[this.readUtf8FromMemory(strPtr,strLen)];
|
||||
if(a === null){
|
||||
return -1;
|
||||
}
|
||||
return this.storeObject(a);
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
if o == -1.0 {
|
||||
return None;
|
||||
} else {
|
||||
Some(JSObject::from(o))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_property<T>(el: impl Into<f64>, id: &str) -> Option<T>
|
||||
where
|
||||
T: GetProperty,
|
||||
{
|
||||
T::get_property(el, id)
|
||||
}
|
||||
|
||||
pub trait SetProperty {
|
||||
fn set_property(el: impl Into<f64>, id: &str, s: Self);
|
||||
}
|
||||
|
||||
impl SetProperty for f64 {
|
||||
fn set_property(el: impl Into<f64>, id: &str, v: Self) {
|
||||
js!("function(el,strPtr,strLen,value){
|
||||
el = this.getObject(el);
|
||||
return el[this.readUtf8FromMemory(strPtr,strLen)] = value;
|
||||
}")
|
||||
.invoke_4(el.into(), id.as_ptr() as u32, id.len() as u32, v);
|
||||
}
|
||||
}
|
||||
|
||||
impl SetProperty for &str {
|
||||
fn set_property(el: impl Into<f64>, name: &str, txt: Self) {
|
||||
js!(r#"function(o,strPtr,strLen,valPtr,valLen){
|
||||
o = this.getObject(o);
|
||||
o[this.readUtf8FromMemory(strPtr,strLen)] = this.readUtf8FromMemory(valPtr,valLen);
|
||||
}"#)
|
||||
.invoke_5(
|
||||
el.into(),
|
||||
name.as_ptr() as u32,
|
||||
name.len() as u32,
|
||||
txt.as_ptr() as u32,
|
||||
txt.len() as u32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl SetProperty for bool {
|
||||
fn set_property(el: impl Into<f64>, name: &str, value: Self) {
|
||||
js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
o[this.readUtf8FromMemory(strPtr,strLen)] = val > 0;
|
||||
}"#)
|
||||
.invoke_4(
|
||||
el.into(),
|
||||
name.as_ptr() as u32,
|
||||
name.len() as u32,
|
||||
if value { 1.0 } else { 0.0 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl SetProperty for JSObject {
|
||||
fn set_property(el: impl Into<f64>, name: &str, value: Self) {
|
||||
js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
o[this.readUtf8FromMemory(strPtr,strLen)] = this.getObject(val);
|
||||
}"#)
|
||||
.invoke_4(
|
||||
el.into(),
|
||||
name.as_ptr() as u32,
|
||||
name.len() as u32,
|
||||
value.handle,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_property<T>(el: impl Into<f64>, id: &str, v: T)
|
||||
where
|
||||
T: SetProperty,
|
||||
{
|
||||
T::set_property(el, id, v)
|
||||
}
|
||||
|
||||
pub fn is_property_null(el: impl Into<f64>, name: &str) -> bool {
|
||||
let v = js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
return o[this.readUtf8FromMemory(strPtr,strLen)] === null ? 1.0 : 0.0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
v == 1.0
|
||||
}
|
||||
|
||||
pub fn is_property_undefined(el: impl Into<f64>, name: &str) -> bool {
|
||||
let v = js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
return o[this.readUtf8FromMemory(strPtr,strLen)] === undefined ? 1.0 : 0.0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
v == 1.0
|
||||
}
|
||||
|
||||
pub fn is_property_number(el: impl Into<f64>, name: &str) -> bool {
|
||||
let v = js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
return typeof o[this.readUtf8FromMemory(strPtr,strLen)] === "number" ? 1.0 : 0.0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
v == 1.0
|
||||
}
|
||||
|
||||
pub fn is_property_bool(el: impl Into<f64>, name: &str) -> bool {
|
||||
let v = js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
return typeof o[this.readUtf8FromMemory(strPtr,strLen)] === "boolean" ? 1.0 : 0.0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
v == 1.0
|
||||
}
|
||||
|
||||
pub fn is_property_string(el: impl Into<f64>, name: &str) -> bool {
|
||||
let v = js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
return typeof o[this.readUtf8FromMemory(strPtr,strLen)] === "string" ? 1.0 : 0.0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
v == 1.0
|
||||
}
|
||||
|
||||
pub fn is_property_object(el: impl Into<f64>, name: &str) -> bool {
|
||||
let v = js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
return typeof o[this.readUtf8FromMemory(strPtr,strLen)] === "object" ? 1.0 : 0.0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
v == 1.0
|
||||
}
|
||||
|
||||
pub fn is_property_array(el: impl Into<f64>, name: &str) -> bool {
|
||||
let v = js!(r#"function(o,strPtr,strLen,val){
|
||||
o = this.getObject(o);
|
||||
return Array.isArray(o[this.readUtf8FromMemory(strPtr,strLen)]) ? 1.0 : 0.0;
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
v == 1.0
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "web_console"
|
||||
version = "0.3.7"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Web functions for console"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
@@ -0,0 +1,20 @@
|
||||
# web_console
|
||||
|
||||
<a href="https://docs.rs/web_console"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in 'web_console' by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
@@ -0,0 +1,55 @@
|
||||
#![no_std]
|
||||
|
||||
use js::*;
|
||||
|
||||
pub fn console_clear() {
|
||||
let func = js!(r###"function(){
|
||||
console.clear();
|
||||
}"###);
|
||||
func.invoke_0();
|
||||
}
|
||||
|
||||
pub fn console_log(msg: &str) {
|
||||
let a0 = msg.as_ptr() as u32;
|
||||
let a1 = msg.len() as u32;
|
||||
let func = js!(r###"function(msgPtr,msgLen){
|
||||
console.log(this.readUtf8FromMemory(msgPtr,msgLen));
|
||||
}"###);
|
||||
func.invoke_2(a0, a1);
|
||||
}
|
||||
|
||||
pub fn console_warning(msg: &str) {
|
||||
let a0 = msg.as_ptr() as u32;
|
||||
let a1 = msg.len() as u32;
|
||||
let func = js!(r###"function(msgPtr,msgLen){
|
||||
console.warn(this.readUtf8FromMemory(msgPtr,msgLen));
|
||||
}"###);
|
||||
func.invoke_2(a0, a1);
|
||||
}
|
||||
|
||||
pub fn console_error(msg: &str) {
|
||||
let a0 = msg.as_ptr() as u32;
|
||||
let a1 = msg.len() as u32;
|
||||
let func = js!(r###"function(msgPtr,msgLen){
|
||||
console.error(this.readUtf8FromMemory(msgPtr,msgLen));
|
||||
}"###);
|
||||
func.invoke_2(a0, a1);
|
||||
}
|
||||
|
||||
pub fn console_time(msg: &str) {
|
||||
let a0 = msg.as_ptr() as u32;
|
||||
let a1 = msg.len() as u32;
|
||||
let func = js!(r###"function(msgPtr,msgLen){
|
||||
console.time(this.readUtf8FromMemory(msgPtr,msgLen));
|
||||
}"###);
|
||||
func.invoke_2(a0, a1);
|
||||
}
|
||||
|
||||
pub fn console_time_end(msg: &str) {
|
||||
let a0 = msg.as_ptr() as u32;
|
||||
let a1 = msg.len() as u32;
|
||||
let func = js!(r###"function(msgPtr,msgLen){
|
||||
console.timeEnd(this.readUtf8FromMemory(msgPtr,msgLen));
|
||||
}"###);
|
||||
func.invoke_2(a0, a1);
|
||||
}
|
||||
15
__wasm/js-wasm/helloworld/js-wasm/crates/web_dom/Cargo.toml
Normal file
15
__wasm/js-wasm/helloworld/js-wasm/crates/web_dom/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "web-dom"
|
||||
version = "0.3.10"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Web functions for DOM manipulation"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
web_common = {path="../web_common", version="0"}
|
||||
20
__wasm/js-wasm/helloworld/js-wasm/crates/web_dom/README.md
Normal file
20
__wasm/js-wasm/helloworld/js-wasm/crates/web_dom/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# web_dom
|
||||
|
||||
<a href="https://docs.rs/web_dom"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `web_dom` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
221
__wasm/js-wasm/helloworld/js-wasm/crates/web_dom/src/lib.rs
Normal file
221
__wasm/js-wasm/helloworld/js-wasm/crates/web_dom/src/lib.rs
Normal file
@@ -0,0 +1,221 @@
|
||||
#![no_std]
|
||||
use js::*;
|
||||
extern crate alloc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use web_common::*;
|
||||
|
||||
pub fn get_element_by_id(id: &str) -> JSObject {
|
||||
js!("function(strPtr,strLen){
|
||||
const el = document.getElementById(this.readUtf8FromMemory(strPtr,strLen));
|
||||
return this.storeObject(el);
|
||||
}")
|
||||
.invoke_2(id.as_ptr() as u32, id.len() as u32)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn query_selector(id: &str) -> JSObject {
|
||||
js!("function(strPtr,strLen){
|
||||
const selector = document.querySelector(this.readUtf8FromMemory(strPtr,strLen));
|
||||
return this.storeObject(selector);
|
||||
}")
|
||||
.invoke_2(id.as_ptr() as u32, id.len() as u32)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn set_style(dom: impl Into<f64>, name: &str, value: &str) {
|
||||
js!("function(el,strPtr,strLen,valPtr,valLen){
|
||||
el = this.getObject(el);
|
||||
const name = this.readUtf8FromMemory(strPtr,strLen);
|
||||
const value = this.readUtf8FromMemory(valPtr,valLen);
|
||||
el.style[name] = value;
|
||||
}")
|
||||
.invoke_5(
|
||||
dom.into(),
|
||||
name.as_ptr() as u32,
|
||||
name.len() as u32,
|
||||
value.as_ptr() as u32,
|
||||
value.len() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn add_event_listener(
|
||||
dom: impl Into<f64>,
|
||||
event: &str,
|
||||
handler: impl FnMut(f64) + Send + 'static,
|
||||
) {
|
||||
let cb = create_callback_1(handler);
|
||||
js!("function(el,strPtr,strLen,callback){
|
||||
el = this.getObject(el);
|
||||
const event = this.readUtf8FromMemory(strPtr,strLen);
|
||||
el.addEventListener(event,this.createCallback(callback));
|
||||
}")
|
||||
.invoke_4(dom.into(), event.as_ptr() as u32, event.len() as u32, cb);
|
||||
}
|
||||
|
||||
pub struct KeyDownEvent {
|
||||
pub handle: JSObject,
|
||||
}
|
||||
|
||||
impl KeyDownEvent {
|
||||
pub fn from_event(ev: impl Into<JSObject>) -> KeyDownEvent {
|
||||
KeyDownEvent { handle: ev.into() }
|
||||
}
|
||||
|
||||
pub fn key_code(&self) -> u32 {
|
||||
js!("function(ev){
|
||||
ev = this.getObject(ev);
|
||||
return ev.keyCode;
|
||||
}")
|
||||
.invoke_1(&self.handle) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach_shadow(el: impl Into<f64>, open: bool) -> JSObject {
|
||||
js!(r#"function(el,open){
|
||||
el = this.getObject(el);
|
||||
el.attachShadow({mode:open?"open":"closed"});
|
||||
return this.storeObject(el.shadowRoot);
|
||||
}"#)
|
||||
.invoke_2(el.into(), if open { 1.0 } else { 0.0 })
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn set_inner_html(el: impl Into<f64>, html: &str) {
|
||||
js!(r#"function(el,strPtr,strLen){
|
||||
el = this.getObject(el);
|
||||
el.innerHTML = this.readUtf8FromMemory(strPtr,strLen);
|
||||
}"#)
|
||||
.invoke_3(el.into(), html.as_ptr() as u32, html.len() as u32);
|
||||
}
|
||||
|
||||
pub fn get_attribute(el: impl Into<f64>, name: &str) -> Option<alloc::string::String> {
|
||||
let attr = js!(r#"function(el,strPtr,strLen){
|
||||
el = this.getObject(el);
|
||||
const a = el.getAttribute(this.readUtf8FromMemory(strPtr,strLen));
|
||||
if(a === null){
|
||||
return -1;
|
||||
}
|
||||
return this.writeUtf8ToMemory(a);
|
||||
}"#)
|
||||
.invoke_3(el.into(), name.as_ptr() as u32, name.len() as u32);
|
||||
if attr == -1.0 {
|
||||
None
|
||||
} else {
|
||||
let allocation_id = attr as usize;
|
||||
let s = extract_string_from_memory(allocation_id);
|
||||
clear_allocation(allocation_id);
|
||||
Some(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyEventHandler {
|
||||
pub handler: Option<Box<dyn Sync + FnMut(KeyEvent) + 'static + Send>>,
|
||||
}
|
||||
|
||||
impl KeyEventHandler {
|
||||
pub fn new(f: impl Sync + FnMut(KeyEvent) + 'static + Send) -> KeyEventHandler {
|
||||
KeyEventHandler {
|
||||
handler: Some(Box::new(f)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyEvent {
|
||||
obj: JSObject,
|
||||
}
|
||||
|
||||
impl KeyEvent {
|
||||
pub fn new(o: f64) -> KeyEvent {
|
||||
KeyEvent {
|
||||
obj: JSObject::from(o),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_code(&self) -> usize {
|
||||
let key_code: f64 = get_property(self.obj.handle, "keyCode").unwrap();
|
||||
key_code as usize
|
||||
}
|
||||
|
||||
pub fn target(&self) -> JSObject {
|
||||
get_property(self.obj.handle, "target").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputElement {
|
||||
obj: JSObject,
|
||||
}
|
||||
|
||||
impl InputElement {
|
||||
pub fn new(o: f64) -> InputElement {
|
||||
InputElement {
|
||||
obj: JSObject::from(o),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(o: JSObject) -> InputElement {
|
||||
InputElement { obj: o }
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Option<String> {
|
||||
get_property(&self.obj, "value")
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, s: &str) {
|
||||
set_property(&self.obj, "value", s)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MouseEventHandler {
|
||||
pub handler: Option<Box<dyn Sync + FnMut(MouseEvent) + 'static + Send>>,
|
||||
}
|
||||
|
||||
impl MouseEventHandler {
|
||||
pub fn new(f: impl Sync + FnMut(MouseEvent) + 'static + Send) -> MouseEventHandler {
|
||||
MouseEventHandler {
|
||||
handler: Some(Box::new(f)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MouseEvent {
|
||||
obj: JSObject,
|
||||
}
|
||||
|
||||
impl MouseEvent {
|
||||
pub fn new(o: f64) -> MouseEvent {
|
||||
MouseEvent {
|
||||
obj: JSObject::from(o),
|
||||
}
|
||||
}
|
||||
pub fn target(&self) -> JSObject {
|
||||
get_property(self.obj.handle, "target").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventHandler {
|
||||
pub handler: Option<Box<dyn Sync + FnMut(Event) + 'static + Send>>,
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
pub fn new(f: impl Sync + FnMut(Event) + 'static + Send) -> EventHandler {
|
||||
EventHandler {
|
||||
handler: Some(Box::new(f)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Event {
|
||||
obj: JSObject,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn new(o: f64) -> Event {
|
||||
Event {
|
||||
obj: JSObject::from(o),
|
||||
}
|
||||
}
|
||||
pub fn target(&self) -> JSObject {
|
||||
get_property(self.obj.handle, "target").unwrap()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "web_local_storage"
|
||||
version = "0.0.3"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Web functions for local storage"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
@@ -0,0 +1,20 @@
|
||||
# web_console
|
||||
|
||||
<a href="https://docs.rs/web_local_storage"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in 'web_local_storage' by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
@@ -0,0 +1,53 @@
|
||||
#![no_std]
|
||||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
|
||||
use js::*;
|
||||
|
||||
pub fn local_storage_set_item(id: &str, data: &str) {
|
||||
let a0 = id.as_ptr() as u32;
|
||||
let a1 = id.len() as u32;
|
||||
let d0 = data.as_ptr() as u32;
|
||||
let d1 = data.len() as u32;
|
||||
let func = js!(r###"function(idPtr,idLen,dataPtr,dataLen){
|
||||
window.localStorage.setItem(this.readUtf8FromMemory(idPtr,idLen),this.readUtf8FromMemory(dataPtr,dataLen));
|
||||
}"###);
|
||||
func.invoke_4(a0, a1, d0, d1);
|
||||
}
|
||||
|
||||
pub fn local_storage_get_item(id: &str) -> Option<String> {
|
||||
let a0 = id.as_ptr() as u32;
|
||||
let a1 = id.len() as u32;
|
||||
let func = js!(r###"function(idPtr,idLen){
|
||||
const a = window.localStorage.getItem(this.readUtf8FromMemory(idPtr,idLen));
|
||||
if(a === null){
|
||||
return -1;
|
||||
}
|
||||
return this.writeUtf8ToMemory(a);
|
||||
}"###);
|
||||
let txt = func.invoke_2(a0, a1);
|
||||
if txt == -1.0 {
|
||||
return None;
|
||||
} else {
|
||||
let allocation_id = txt as usize;
|
||||
let s = extract_string_from_memory(allocation_id);
|
||||
clear_allocation(allocation_id);
|
||||
Some(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_storage_remove_item(id: &str) {
|
||||
let a0 = id.as_ptr() as u32;
|
||||
let a1 = id.len() as u32;
|
||||
let func = js!(r###"function(idPtr,idLen){
|
||||
window.localStorage.removeItem(this.readUtf8FromMemory(idPtr,idLen));
|
||||
}"###);
|
||||
func.invoke_2(a0, a1);
|
||||
}
|
||||
|
||||
pub fn local_storage_clear() {
|
||||
let func = js!(r###"function(idPtr,idLen,dataPtr,dataLen){
|
||||
window.localStorage.clear();
|
||||
}"###);
|
||||
func.invoke_0();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "web_random"
|
||||
version = "0.1.5"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Web functions for randomness"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
@@ -0,0 +1,20 @@
|
||||
# web_random
|
||||
|
||||
<a href="https://docs.rs/web_random"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in 'web_random' by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
@@ -0,0 +1,9 @@
|
||||
#![no_std]
|
||||
use js::*;
|
||||
|
||||
pub fn random() -> f64 {
|
||||
js!("function(){
|
||||
return Math.random();
|
||||
}")
|
||||
.invoke_0()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "web_timer"
|
||||
version = "0.2.5"
|
||||
authors = ["Richard Anaya"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Web functions for timers"
|
||||
repository = "https://github.com/richardanaya/js-wasm"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
20
__wasm/js-wasm/helloworld/js-wasm/crates/web_timer/README.md
Normal file
20
__wasm/js-wasm/helloworld/js-wasm/crates/web_timer/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# web_timer
|
||||
|
||||
<a href="https://docs.rs/web_timer"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in 'web_timer' by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
@@ -0,0 +1,79 @@
|
||||
#![no_std]
|
||||
use core::future::Future;
|
||||
use js::*;
|
||||
|
||||
pub type Handle = f64;
|
||||
|
||||
pub fn set_timeout(
|
||||
callback: impl FnMut() -> () + Send + 'static,
|
||||
milliseconds: impl Into<f64>,
|
||||
) -> (Handle, JSFunction) {
|
||||
let cb = create_callback_0(callback);
|
||||
let handle = js!("function(handler,time){
|
||||
window.setTimeout(this.createCallback(handler),time);
|
||||
}")
|
||||
.invoke_2(cb, milliseconds);
|
||||
(handle, cb.into())
|
||||
}
|
||||
|
||||
pub fn sleep(milliseconds: impl Into<f64>) -> impl Future {
|
||||
let (future, cb) = create_callback_future_0();
|
||||
js!("function(handler,time){
|
||||
window.setTimeout(this.createCallback(handler),time);
|
||||
}")
|
||||
.invoke_2(cb, milliseconds);
|
||||
future
|
||||
}
|
||||
|
||||
pub fn set_interval(
|
||||
callback: impl FnMut() -> () + Send + 'static,
|
||||
milliseconds: impl Into<f64>,
|
||||
) -> (Handle, JSFunction) {
|
||||
let cb = create_callback_0(callback);
|
||||
let handle = js!("function(handler,time){
|
||||
window.setInterval(this.createCallback(handler),time);
|
||||
}")
|
||||
.invoke_2(cb, milliseconds);
|
||||
(handle, cb.into())
|
||||
}
|
||||
|
||||
pub fn request_animation_frame(callback: impl FnMut() -> () + Send + 'static) -> JSFunction {
|
||||
let cb = create_callback_0(callback);
|
||||
js!("function(handler){
|
||||
window.requestAnimationFrame(this.createCallback(handler));
|
||||
}")
|
||||
.invoke_1(cb);
|
||||
cb.into()
|
||||
}
|
||||
|
||||
pub fn request_animation_loop(callback: impl FnMut(f64) -> () + Send + 'static) -> JSFunction {
|
||||
let cb = create_callback_1(callback);
|
||||
js!("function(cb){
|
||||
cb = this.createCallback(cb);
|
||||
let time = Date.now();
|
||||
function run(){
|
||||
let new_time = Date.now();
|
||||
let delta = new_time-time;
|
||||
time = new_time;
|
||||
window.requestAnimationFrame(run);
|
||||
cb(delta);
|
||||
}
|
||||
window.requestAnimationFrame(run);
|
||||
}")
|
||||
.invoke_1(cb);
|
||||
cb.into()
|
||||
}
|
||||
|
||||
pub fn clear_timeout(handle: Handle) {
|
||||
js!("function(handle){
|
||||
window.clearTimeout(handle);
|
||||
}")
|
||||
.invoke_1(handle);
|
||||
}
|
||||
|
||||
pub fn clear_interval(handle: Handle) {
|
||||
js!("function(handle){
|
||||
window.clearInterval(handle);
|
||||
}")
|
||||
.invoke_1(handle);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "webcomponent"
|
||||
version = "0.6.2"
|
||||
edition = "2018"
|
||||
authors = ["Richard Anaya"]
|
||||
categories = ["wasm", "no-std", "web"]
|
||||
include = [
|
||||
"src/**/*.rs",
|
||||
"Cargo.toml",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
homepage = "https://github.com/richardanaya/webcomponent"
|
||||
repository = "https://github.com/richardanaya/webcomponent"
|
||||
description = "A library for creating web components"
|
||||
|
||||
[dependencies]
|
||||
js = {path="../js",version="0"}
|
||||
callback= {path="../callback",version="0.5.0"}
|
||||
spin = "0.7.0"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1"
|
||||
default-features = false
|
||||
features = ["spin_no_std"]
|
||||
168
__wasm/js-wasm/helloworld/js-wasm/crates/webcomponent/README.md
Normal file
168
__wasm/js-wasm/helloworld/js-wasm/crates/webcomponent/README.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# webcomponent
|
||||
|
||||
<a href="https://docs.rs/webcomponent"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
|
||||
[Web components](https://www.webcomponents.org/) are a W3C standard for writing your own HTML element. `webcomponent` is a Rust library for easily writing your own web components in Rust with [`js_ffi`](https://github.com/richardanaya/js_ffi).
|
||||
|
||||
Features:
|
||||
- [x] Shadow DOM
|
||||
- [x] Observable attributes
|
||||
- [x] Helper functions
|
||||
- [x] `#![no_std]` and `alloc`
|
||||
|
||||
# Hello World
|
||||
```toml
|
||||
[lib]
|
||||
crate-type =["cdylib"] # configures rust project to build a web assembly module
|
||||
|
||||
[dependencies]
|
||||
webcomponent="0.5" # for registering our web component
|
||||
```
|
||||
```rust
|
||||
use webcomponent::*;
|
||||
|
||||
struct HelloWorld {
|
||||
element: HTMLElement
|
||||
}
|
||||
|
||||
impl CustomElement for HelloWorld {
|
||||
fn new(element:HTMLElement) -> Self {
|
||||
HelloWorld(element)
|
||||
}
|
||||
fn connected(&mut self){
|
||||
set_html(&self.element,"Hello World!");
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn main() {
|
||||
HelloWorld::register("hello-world");
|
||||
}
|
||||
```
|
||||
```html
|
||||
<!-- a polyfill for web components on some browsers -->
|
||||
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
|
||||
<!-- for running your js_ffi library -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
|
||||
<!-- get things started -->
|
||||
<script>js_ffi.run("helloworld.wasm");</script>
|
||||
<!-- now you can put your hello-world element anywhere! -->
|
||||
<hello-world></hello-world>
|
||||
```
|
||||
```makefile
|
||||
# cli commands for building web assembly I find useful
|
||||
build:
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/helloworld.wasm .
|
||||
lint:
|
||||
@cargo fmt
|
||||
serve:
|
||||
python3 -m http.server 8080
|
||||
```
|
||||
|
||||
|
||||
See demo [here](https://richardanaya.github.io/webcomponent/examples/helloworld/)
|
||||
|
||||
# Shadow DOM
|
||||
|
||||
```rust
|
||||
struct HelloWorld {
|
||||
element:HTMLElement
|
||||
}
|
||||
|
||||
impl CustomElement for HelloWorld {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
HelloWorld(element)
|
||||
}
|
||||
fn connected(&mut self) {
|
||||
let shadow_dom = attach_shadow(&self.element, true);
|
||||
set_html(&shadow_dom, r#"<div>Hello <slot name="fname"></slot>!</div>"#);
|
||||
set_html(&self.element, r#"<span slot="fname">Richard</span>"#);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See demo [here](https://richardanaya.github.io/webcomponent/examples/shadowdom/)
|
||||
|
||||
# Observable Attributes
|
||||
|
||||
```rust
|
||||
struct HelloPerson {
|
||||
element: HTMLElement
|
||||
}
|
||||
|
||||
impl CustomElement for HelloPerson {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
HelloPerson(element)
|
||||
}
|
||||
|
||||
fn observed_attributes() -> Vec<&'static str> {
|
||||
vec!["first_name"]
|
||||
}
|
||||
|
||||
fn connected(&mut self) {
|
||||
self.render();
|
||||
}
|
||||
|
||||
fn attribute_changed(&mut self, _name: String, _old_value: Option<String>, _new_value: Option<String>) {
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
|
||||
impl HelloPerson {
|
||||
fn render(&mut self){
|
||||
let first_name = get_attribute(&self.element, "first_name").unwrap_or("human".to_string());
|
||||
let msg = "Hello ".to_string() + &first_name;
|
||||
set_html(&self.element, &msg);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See demo [here](https://richardanaya.github.io/webcomponent/examples/observable_attributes/)
|
||||
|
||||
# What about the rest of Javscript?
|
||||
|
||||
With `webcomponent` you have a handle to an html element, this is simply a reference to your component that exists in the DOM. With it we can use [`js_ffi`](https://github.com/richardanaya/js_ffi) ( or any [`js_ffi` based library](https://github.com/richardanaya/js_ffi#standard-web-libraries) ) to do whatever we want to do.
|
||||
|
||||
```rust
|
||||
use webcomponent::*;
|
||||
use js_ffi::*;
|
||||
|
||||
struct LoudButton {
|
||||
element: HTMLElement
|
||||
}
|
||||
|
||||
impl CustomElement for LoudButton {
|
||||
fn new(element:HTMLElement) -> Self {
|
||||
LoudButton(element)
|
||||
}
|
||||
fn connected(&mut self){
|
||||
set_html(&self.element,r#"<button>Shout!</button>"#);
|
||||
js!(Node.prototype.addEventListener).call_2(
|
||||
&self.element,
|
||||
"click",
|
||||
create_callback_0(|| {
|
||||
js!(window.alert).invoke_1("I was clicked!");
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `webcomponent` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["richard <richard>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
web = {path="../../../web"}
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -0,0 +1,7 @@
|
||||
build:
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/example.wasm .
|
||||
lint:
|
||||
@cargo fmt
|
||||
serve:
|
||||
python3 -m http.server 8089
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
<!-- a polyfill for web components on some browsers -->
|
||||
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
|
||||
<!-- for running your js_ffi library -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
|
||||
<!-- get things started -->
|
||||
<script>js_ffi.run("example.wasm");</script>
|
||||
<!-- now you can put your hello-world element anywhere! -->
|
||||
<hello-world></hello-world>
|
||||
@@ -0,0 +1,17 @@
|
||||
use web::*;
|
||||
|
||||
struct HelloWorld(HTMLElement);
|
||||
|
||||
impl CustomElement for HelloWorld {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
HelloWorld(element)
|
||||
}
|
||||
fn connected(&mut self) {
|
||||
set_inner_html(self.0, "Hello World!");
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn main() {
|
||||
HelloWorld::register("hello-world");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["richard <richard>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
webcomponent = {path="../../"}
|
||||
js_ffi = "0.6"
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -0,0 +1,7 @@
|
||||
build:
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/example.wasm .
|
||||
lint:
|
||||
@cargo fmt
|
||||
serve:
|
||||
python3 -m http.server 8089
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
<!-- a polyfill for web components on some browsers -->
|
||||
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
|
||||
<!-- for running your js_ffi library -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
|
||||
<!-- get things started -->
|
||||
<script>js_ffi.run("example.wasm");</script>
|
||||
<!-- now you can put your hello-world element anywhere! -->
|
||||
<loud-button></loud-button>
|
||||
@@ -0,0 +1,27 @@
|
||||
use js_ffi::*;
|
||||
use webcomponent::*;
|
||||
|
||||
struct LoudButton {
|
||||
element: HTMLElement,
|
||||
}
|
||||
|
||||
impl CustomElement for LoudButton {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
LoudButton { element }
|
||||
}
|
||||
fn connected(&mut self) {
|
||||
set_html(&self.element, "<button>Shout!</button>");
|
||||
js!(Node.prototype.addEventListener).call_2(
|
||||
&self.element,
|
||||
"click",
|
||||
create_callback_0(|| {
|
||||
js!(window.alert).invoke_1("I was clicked!");
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn main() {
|
||||
LoudButton::register("loud-button");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["richard <richard>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
webcomponent = {path="../../"}
|
||||
js_ffi = "0.6"
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -0,0 +1,7 @@
|
||||
build:
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/example.wasm .
|
||||
lint:
|
||||
@cargo fmt
|
||||
serve:
|
||||
python3 -m http.server 8089
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
<!-- a polyfill for web components on some browsers -->
|
||||
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
|
||||
<!-- for running your js_ffi library -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
|
||||
<!-- get things started -->
|
||||
<script>js_ffi.run("example.wasm");</script>
|
||||
<!-- now you can put your hello-world element anywhere! -->
|
||||
<hello-person first_name="Richard"></hello-person>
|
||||
@@ -0,0 +1,39 @@
|
||||
use webcomponent::*;
|
||||
|
||||
struct HelloPerson(HTMLElement);
|
||||
|
||||
impl CustomElement for HelloPerson {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
HelloPerson(element)
|
||||
}
|
||||
|
||||
fn observed_attributes() -> Vec<&'static str> {
|
||||
vec!["first_name"]
|
||||
}
|
||||
|
||||
fn connected(&mut self) {
|
||||
self.render();
|
||||
}
|
||||
|
||||
fn attribute_changed(
|
||||
&mut self,
|
||||
_name: String,
|
||||
_old_value: Option<String>,
|
||||
_new_value: Option<String>,
|
||||
) {
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
|
||||
impl HelloPerson {
|
||||
fn render(&mut self) {
|
||||
let first_name = get_attribute(&self.0, "first_name").unwrap_or("human".to_string());
|
||||
let msg = "Hello ".to_string() + &first_name;
|
||||
set_html(&self.0, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn main() {
|
||||
HelloPerson::register("hello-person");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["richard <richard>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
webcomponent = {path="../../"}
|
||||
js_ffi = "0.6"
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -0,0 +1,7 @@
|
||||
build:
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/example.wasm .
|
||||
lint:
|
||||
@cargo fmt
|
||||
serve:
|
||||
python3 -m http.server 8089
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
<!-- a polyfill for web components on some browsers -->
|
||||
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
|
||||
<!-- for running your js_ffi library -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
|
||||
<!-- get things started -->
|
||||
<script>js_ffi.run("example.wasm");</script>
|
||||
<!-- now you can put your hello-world element anywhere! -->
|
||||
<hello-world></hello-world>
|
||||
@@ -0,0 +1,19 @@
|
||||
use webcomponent::*;
|
||||
|
||||
struct HelloWorld(HTMLElement);
|
||||
|
||||
impl CustomElement for HelloWorld {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
HelloWorld(element)
|
||||
}
|
||||
fn connected(&mut self) {
|
||||
let shadow_dom = attach_shadow(&self.0, true);
|
||||
set_html(&shadow_dom, r#"<div>Hello <slot name="fname"></slot>!</div>"#);
|
||||
set_html(&self.0, r#"<span slot="fname">Richard</span>"#);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn main() {
|
||||
HelloWorld::register("hello-world");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["richard <richard>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
webcomponent = {path="../../"}
|
||||
js_ffi = "0.6"
|
||||
web_console = "0"
|
||||
web-dom = "0"
|
||||
globals = "1"
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -0,0 +1,7 @@
|
||||
build:
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/example.wasm .
|
||||
lint:
|
||||
@cargo fmt
|
||||
serve:
|
||||
python3 -m http.server 8089
|
||||
BIN
__wasm/js-wasm/helloworld/js-wasm/crates/webcomponent/examples/todo/example.wasm
Executable file
BIN
__wasm/js-wasm/helloworld/js-wasm/crates/webcomponent/examples/todo/example.wasm
Executable file
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Todo</title>
|
||||
<!-- a polyfill for web components on some browsers -->
|
||||
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
|
||||
<!-- for running your js_ffi library -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
|
||||
<!-- get things started -->
|
||||
<script>js_ffi.run("example.wasm");</script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container is-fluid">
|
||||
<todo-list>
|
||||
<todo-item done="yes">Write Rust</todo-item>
|
||||
<todo-item done="no">Read that book</todo-item>
|
||||
<todo-item done="no">Hang picture</todo-item>
|
||||
</todo-list>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,118 @@
|
||||
use js_ffi::*;
|
||||
use web_console::*;
|
||||
use web_dom::*;
|
||||
use webcomponent::*;
|
||||
|
||||
struct TodoList {
|
||||
items: Vec<TodoItem>,
|
||||
}
|
||||
|
||||
impl Default for TodoList {
|
||||
fn default() -> Self {
|
||||
TodoList {
|
||||
items: vec![TodoItem {
|
||||
done: true,
|
||||
description: "write some rust".to_string(),
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TodoItem {
|
||||
done: bool,
|
||||
description: String,
|
||||
}
|
||||
|
||||
struct TodoListComponent {
|
||||
element: HTMLElement,
|
||||
shadow_root: HTMLElement,
|
||||
}
|
||||
|
||||
impl CustomElement for TodoListComponent {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
TodoListComponent {
|
||||
shadow_root: attach_shadow(&element, true),
|
||||
element,
|
||||
}
|
||||
}
|
||||
fn connected(&mut self) {
|
||||
self.render();
|
||||
let state = globals::get::<TodoList>();
|
||||
append_html(self.element,r#"<todo-item done="yes"></todo-item>"#);
|
||||
}
|
||||
}
|
||||
|
||||
impl TodoListComponent {
|
||||
fn render(&mut self) {
|
||||
set_html(&self.shadow_root, include_str!("todo-list.html"));
|
||||
}
|
||||
}
|
||||
|
||||
struct TodoItemComponent {
|
||||
element: HTMLElement,
|
||||
shadow_root: HTMLElement,
|
||||
is_done: bool,
|
||||
}
|
||||
|
||||
impl CustomElement for TodoItemComponent {
|
||||
fn new(element: HTMLElement) -> Self {
|
||||
TodoItemComponent {
|
||||
shadow_root: attach_shadow(&element, true),
|
||||
element: element,
|
||||
is_done: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn connected(&mut self) {
|
||||
set_html(&self.shadow_root, include_str!("todo-item.html"));
|
||||
let btn = query_selector(&self.shadow_root, "button");
|
||||
add_event_listener(
|
||||
&btn,
|
||||
"click",
|
||||
create_callback_0(|| {
|
||||
js!(window.alert).invoke_1("I was clicked!");
|
||||
}),
|
||||
);
|
||||
self.render();
|
||||
}
|
||||
|
||||
fn observed_attributes() -> Vec<&'static str> {
|
||||
vec!["done"]
|
||||
}
|
||||
|
||||
fn attribute_changed(
|
||||
&mut self,
|
||||
name: String,
|
||||
_old_value: Option<String>,
|
||||
new_value: Option<String>,
|
||||
) {
|
||||
if name == "done" {
|
||||
if let Some(value) = new_value {
|
||||
if value == "yes" {
|
||||
self.is_done = true;
|
||||
} else {
|
||||
self.is_done = false;
|
||||
}
|
||||
} else {
|
||||
self.is_done = false;
|
||||
}
|
||||
}
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
|
||||
impl TodoItemComponent {
|
||||
fn render(&mut self) {
|
||||
if self.is_done {
|
||||
log("done");
|
||||
} else {
|
||||
log("not done");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn main() {
|
||||
TodoListComponent::register("todo-list");
|
||||
TodoItemComponent::register("todo-item");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<div><input type="checkbox"> <slot></slot> <button>delete</button></div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="title">Todo List</h2>
|
||||
<div><slot></slot></div>
|
||||
<div><input/><button>Add</button></div>
|
||||
</div>
|
||||
</section>
|
||||
147
__wasm/js-wasm/helloworld/js-wasm/crates/webcomponent/src/lib.rs
Normal file
147
__wasm/js-wasm/helloworld/js-wasm/crates/webcomponent/src/lib.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
#![no_std]
|
||||
use js::*;
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use spin::Mutex;
|
||||
|
||||
pub type HTMLElement = f64;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Destructable {
|
||||
function: Option<JSFunction>,
|
||||
}
|
||||
|
||||
pub trait CustomElement {
|
||||
fn new(element: HTMLElement) -> Self
|
||||
where
|
||||
Self: core::marker::Sized + core::marker::Sync + core::marker::Send + 'static;
|
||||
fn register(name: &str)
|
||||
where
|
||||
Self: core::marker::Sized + core::marker::Sync + core::marker::Send + 'static,
|
||||
{
|
||||
let construct = create_callback_1(|element| {
|
||||
let el = Arc::new(Mutex::new(Self::new(element.into())));
|
||||
let el1 = el.clone();
|
||||
let el2 = el.clone();
|
||||
let el3 = el.clone();
|
||||
|
||||
let destruct_connect = Arc::new(Mutex::new(Destructable { function: None }));
|
||||
let connect = create_callback_0(move || {
|
||||
el1.lock().connected();
|
||||
});
|
||||
destruct_connect.lock().function = Some(connect.into());
|
||||
|
||||
let destruct_attribute_change = Arc::new(Mutex::new(Destructable { function: None }));
|
||||
let attribute_change = create_callback_3(move |name_obj, old_obj, new_obj| {
|
||||
let name = cstr_to_string(name_obj as i32);
|
||||
let old = if old_obj == -1.0 {
|
||||
None
|
||||
} else {
|
||||
Some(cstr_to_string(old_obj as i32))
|
||||
};
|
||||
let new = if new_obj == -1.0 {
|
||||
None
|
||||
} else {
|
||||
Some(cstr_to_string(new_obj as i32))
|
||||
};
|
||||
el3.lock().attribute_changed(name, old, new);
|
||||
});
|
||||
|
||||
destruct_attribute_change.lock().function = Some(connect.into());
|
||||
|
||||
let destruct_disconnect = Arc::new(Mutex::new(Destructable { function: None }));
|
||||
let destruct_disconnect2 = destruct_disconnect.clone();
|
||||
let disconnect = create_callback_0(move || {
|
||||
el2.lock().disconnected();
|
||||
remove_callback(destruct_connect.lock().function.as_ref().unwrap().into());
|
||||
remove_callback(destruct_disconnect.lock().function.as_ref().unwrap().into());
|
||||
remove_callback(
|
||||
destruct_attribute_change
|
||||
.lock()
|
||||
.function
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
destruct_disconnect2.lock().function = Some(disconnect.into());
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref FN: JSFunction= {
|
||||
register_function(
|
||||
r#"function(e,a,b,c){
|
||||
e.addHooks(a,b,c);
|
||||
}"#,
|
||||
)
|
||||
};};
|
||||
FN.invoke_4(element, connect, disconnect, attribute_change);
|
||||
});
|
||||
let attrs = Self::observed_attributes().join(",");
|
||||
lazy_static::lazy_static! {
|
||||
static ref FN: JSFunction= {
|
||||
register_function(
|
||||
r#"function(construct,elementNamePtr, elementNameLen,attrNamesPtr,attrNamesLen){
|
||||
const elementName = this.readUtf8FromMemory(elementNamePtr,elementNameLen);
|
||||
const attrNames = this.readUtf8FromMemory(attrNamesPtr,attrNamesLen);
|
||||
let attrs = attrNames.split(",");
|
||||
class GeneratedCustomElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
construct(this);
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
self.connect();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
self.disconnect();
|
||||
}
|
||||
|
||||
attributeChangedCallback(attributeName, oldValue, newValue) {
|
||||
self.attributeChange(attributeName,oldValue,newValue)
|
||||
}
|
||||
|
||||
addHooks(connect,disconnect,attributeChange){
|
||||
self.connect = connect;
|
||||
self.disconnect = disconnect;
|
||||
self.attributeChange = attributeChange;
|
||||
}
|
||||
}
|
||||
|
||||
// tell the dom to associate it with an html tag name
|
||||
customElements.define(elementName, GeneratedCustomElement);
|
||||
}"#,
|
||||
)
|
||||
};};
|
||||
FN.invoke_5(
|
||||
construct,
|
||||
name.as_ptr() as u32,
|
||||
name.len() as u32,
|
||||
attrs.as_ptr() as u32,
|
||||
attrs.len() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
fn observed_attributes() -> Vec<&'static str> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn created(&mut self) {}
|
||||
fn connected(&mut self) {}
|
||||
fn disconnected(&mut self) {}
|
||||
fn attribute_changed(
|
||||
&mut self,
|
||||
_name: String,
|
||||
_old_value: Option<String>,
|
||||
_new_value: Option<String>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
16
__wasm/js-wasm/helloworld/js-wasm/electron/Cargo.toml
Normal file
16
__wasm/js-wasm/helloworld/js-wasm/electron/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["Richard <Richard>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
js = "0"
|
||||
|
||||
[lib]
|
||||
crate-type =["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
27
__wasm/js-wasm/helloworld/js-wasm/electron/Makefile
Normal file
27
__wasm/js-wasm/helloworld/js-wasm/electron/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
build: tmp/electron.zip dist/js-wasm.js dist/app.wasm dist/index.html dist/index.js
|
||||
|
||||
run: build
|
||||
cd dist && ./electron .
|
||||
|
||||
dist/index.js: index.js
|
||||
cp index.js dist/index.js
|
||||
|
||||
dist/app.wasm: src/**.rs
|
||||
@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
|
||||
@cp target/wasm32-unknown-unknown/release/example.wasm dist/app.wasm
|
||||
|
||||
dist/js-wasm.js:
|
||||
wget -O dist/js-wasm.js https://unpkg.com/js-wasm/js-wasm.js
|
||||
|
||||
tmp/electron.zip:
|
||||
mkdir -p tmp
|
||||
mkdir -p dist
|
||||
wget -O tmp/electron.zip https://github.com/electron/electron/releases/download/v11.0.2/electron-v11.0.2-linux-x64.zip
|
||||
cd tmp && unzip electron.zip -d ../dist
|
||||
|
||||
clean:
|
||||
rm -rf dist
|
||||
rm -rf tmp
|
||||
|
||||
dist/index.html: index.html
|
||||
cp index.html dist/index.html
|
||||
68
__wasm/js-wasm/helloworld/js-wasm/electron/README.md
Normal file
68
__wasm/js-wasm/helloworld/js-wasm/electron/README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Electron + WebAssembly = :heart:
|
||||
|
||||
<p align="center">
|
||||
<img height="300" src="../images/undraw_web_developer_p3e5.png">
|
||||
</p>
|
||||
|
||||
|
||||
Creating desktop apps with WebAssembly is as simple as making website! `electron` is a technology that packages chrome into an desktop app-like minimalistic web view experience. It gives the user access to the local systems resources via the `nodejs` JavaScript api. Since `js-wasm` is a technology agnostic library for executing javascript from WebAssembly, we can easily bind to the parts of the `electron` API we need.
|
||||
|
||||
# Getting Started
|
||||
|
||||
This directory contains an example to get started using quickly.
|
||||
|
||||
```
|
||||
git clone git@github.com:richardanaya/js-wasm.git
|
||||
cd js-wasm/electron
|
||||
make run
|
||||
```
|
||||
|
||||
Let's take a look at the various files:
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `index.js` | Electron wants a javascript file to tell it where to start, this is just basic setup you can tweak like starting width/height and does your app have a menubar. |
|
||||
| `index.html` | This is the index file that gets loaded by `index.js` to be the first thing you see. Consider this just like any old web page. |
|
||||
|
||||
All our `index.html` does is quickly tell WebAssembly to start like any old web application
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>My App!</title>
|
||||
<link rel="shortcut icon" href="#" />
|
||||
<script src="js-wasm.js"></script>
|
||||
<script type="application/wasm" src="app.wasm"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="screen" width=500 height=500></canvas>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Getting what you need
|
||||
|
||||
Now you can create JavaScript binding functions to invoke using [`js-wasm`](https://github.com/richardanaya/js-wasm/) as normal.
|
||||
|
||||
```rust
|
||||
pub fn read_file(msg: &str) -> String {
|
||||
lazy_static::lazy_static! {
|
||||
static ref FN: JSFunction= {
|
||||
register_function(
|
||||
"function(pathPtr,pathLen){
|
||||
const fs = require('fs');
|
||||
const data String(fs.readFileSync(this.readUtf8FromMemory(pathPtr,pathLen)));
|
||||
return this.writeCStringToMemory(data);
|
||||
}",
|
||||
)
|
||||
};};
|
||||
cstr_to_string(FN.invoke_2(msg.as_ptr() as u32, msg.len() as u32) as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() {
|
||||
let fileContent = read_file("foo.txt");
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
17
__wasm/js-wasm/helloworld/js-wasm/electron/index.html
Normal file
17
__wasm/js-wasm/helloworld/js-wasm/electron/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>My App!</title>
|
||||
<link rel="shortcut icon" href="#" />
|
||||
<script src="js-wasm.js"></script>
|
||||
<script type="application/wasm" src="app.wasm"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="screen" width=500 height=500></canvas>
|
||||
</body>
|
||||
<script>
|
||||
debugger;
|
||||
let fs = require('fs');
|
||||
console.log(fs);
|
||||
</script>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user