-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from rust-diplomat/demo_gen
demo_gen Documentation
- Loading branch information
Showing
10 changed files
with
344 additions
and
3 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# Attributes | ||
|
||
demo_gen does a lot in its attempt to automagically generate demonstrations for you. Sometimes however, hands on configuration is required. | ||
|
||
Find below a list of the attributes that demo_gen supports. | ||
|
||
## \#\[diplomat::attr\] | ||
|
||
demo_gen supports all attributes listed in the [attrs chapter](../attrs.md). You mostly will want to use the `disable` attribute, to disable any functions that you may not want to include in output. | ||
|
||
Because demo_gen is so heavily based on the JS backend, any `#[diplomat::attr]`s that apply to the JS backend will also apply to the demo_gen backend. So for example, any methods disabled in the JS backend will also be disabled in demo_gen's output. | ||
|
||
## \#\[diplomat::demo\] | ||
|
||
This is the core attribute that demo_gen looks for in configuring its output. There are a few supported attributes currently: | ||
|
||
### \#\[diplomat::demo(generate)\] | ||
|
||
Used in explicit generation of output. See [markup](./markup.md) for more. | ||
|
||
### \#\[diplomat::demo(default_constructor)\] | ||
|
||
demo_gen will throw errors for any Opaque types that do not have a method labelled with this attribute. demo_gen also looks for any Opaque methods labelled with `#[diplomat::attr(auto, constructor)]` as an alternative. | ||
|
||
You should label each Opaque in your FFI definition with a `default_constructor` attribute, where the method is one you expect most users to call regularly when trying to create the Opaque in question. If your Opaque does not have an associated constructor method in its `impl` block, you should consider disabling functions (as this sort of behavior is too advanced for demo_gen to parse correctly). | ||
|
||
For reasons on why demo_gen requires explicit labelling of Opaque constructors, see [the demo_gen design doc](https://github.com/rust-diplomat/diplomat/blob/main/docs/demo_gen.md). | ||
|
||
### \#\[diplomat::demo(external)\] | ||
|
||
Can be used above a parameter, struct field, or Opaque type. | ||
|
||
It represents any input that you want to specify custom behavior for in the [rendering](./renderer.md) Javascript. | ||
|
||
For example: In ICU4X, we have a `DataProvider` Opaque type that must be compiled ahead of time, and so we flag it as an external type: | ||
|
||
```rs | ||
#[diplomat::bridge] | ||
mod ffi { | ||
#[diplomat::opaque] | ||
#[diplomat::demo(external)] | ||
pub struct DataProvider; | ||
} | ||
``` | ||
|
||
We then override the [default renderer's runtime.mjs](renderer.md#runtimemjs) file to provide the compiled `DataProvider` when it is requested. | ||
|
||
### \#\[diplomat::demo(input(...))\] | ||
|
||
For configuring user input to your demos. `input(...)` takes in a comma separated list of values. | ||
|
||
May be used on parameters or struct fields to configure specific properties passed to the [renderer](renderer.md). | ||
|
||
Here are some valid `input` values: | ||
|
||
- `input(label = "Label Here")`. Changes the label a given function parameter will have in the output. | ||
|
||
#### Input Example | ||
|
||
If we modify our [quickstart](quickstart.md) example, we can add `#[diplomat::demo(input(...))]` labels to the function parameters: | ||
|
||
```rs | ||
#[diplomat::bridge] | ||
mod ffi { | ||
use std::fmt::Write; | ||
|
||
#[diplomat::opaque] | ||
#[diplomat::rust_link(basic_adder, Mod)] | ||
pub struct AddResult; | ||
|
||
impl AddResult { | ||
pub fn get_add_str( | ||
#[diplomat::demo(input(label = "x"))] | ||
left : u32, | ||
#[diplomat::demo(input(label = "y"))] | ||
right : u32, write: &mut DiplomatWrite) { | ||
write.write_str(&format!("{}", basic_adder::add(left, right))).unwrap(); | ||
write.flush(); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Which creates the following HTML output: | ||
|
||
!["AddResult.getAddStr" in large text. Below are two inputs: one labelled "x" that has a value of 10, and one labelled "y" that has a value of 2. Below is a submit button. There is output below the button, with the label "Output" and a value of 12.](../../images/demo_output_renamed.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Making Your Own Renderer | ||
|
||
Inside of `index.mjs`, demo_gen outputs an object called `RenderInfo` that points to all the functions demo_gen has created for the purposes of demonstration. | ||
|
||
`RenderInfo` gives you the function to call directly, as well as the required parameters needed for each function in order. | ||
|
||
This is meant to slot in to almost any Javascript solution with ease, but if there's an issue with `RenderInfo`s setup that is not quite compatible with your solution, please [open an issue](https://github.com/rust-diplomat/diplomat/issues/new?labels=B-demo_gen). | ||
|
||
The exact structure of `RenderInfo` is available in the demo_gen [design docs](https://github.com/rust-diplomat/diplomat/blob/main/docs/design_doc.md#step-two-constructing-renderinfo). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# demo_gen | ||
|
||
## What is demo_gen? | ||
|
||
demo_gen is a backend for creating demonstration webpages automatically from Diplomat FFI definitions. These webpages are meant to showcase the capabilities of your library. | ||
|
||
|
||
You can view the tracking issue for the demo_gen backend [here](https://github.com/rust-diplomat/diplomat/issues/604). | ||
|
||
If you're interested in the design behind demo_gen, we have a [design document](https://github.com/rust-diplomat/diplomat/blob/main/docs/demo_gen.md) viewable on the Diplomat repository. | ||
|
||
## Why do I want to use demo_gen? | ||
|
||
The current big-name use case for demo_gen is the ICU4X internationalization library. ICU4X has a wide breadth of functions that can be somewhat hard to grasp if you're not already using the library heavily. demo_gen is a quick way | ||
|
||
You can view the ICU4X demo_gen results [here](https://ambiguous.name/icu4x/) for a full demonstration of what demo_gen is currently capable. | ||
|
||
If you're interested in trying demo_gen yourself, hop on to the [Quickstart](./quickstart.md) page to get started! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Configuring Markup | ||
Diplomat takes the `-l` or `--library-config` option (in `diplomat_tool::gen` this is the `library_config` parameter). This represents a path to a `.toml` file that demo_gen will then read and convert into `DemoConfig`. | ||
|
||
Here's a sample .toml file for configuration (with comments for clarity): | ||
|
||
```toml | ||
# If false, demo_gen will automatically search all methods for functions it can generate demonstration JS for. | ||
# If true, demo_gen will look for any methods explicitly flagged with #[diplomat::demo(generate)] to perform generation. | ||
explicit-generation=true # default = false (bool) | ||
|
||
# This removes the rendering/ folder. | ||
hide-default-renderer=true # default = false (bool) | ||
|
||
# Adjusts all imports that demo_gen creates to a specific module. Setting this will not generate the js/ folder. | ||
# | ||
# So for instance, this setting will adjust imports to: `import { type } from "icu4x"; | ||
module-name="icu4x" # (string) | ||
|
||
# Adjusts all imports that demo_gen creates to a relative path where Diplomat JS output should be. Setting this will not generate the js/ folder. | ||
# | ||
# Setting this will adjust imports to: `import {type} from "../js/folder/here/index.mjs"; | ||
# | ||
# Intended to be a mutually exclusive setting with module-name, although you can set both simultaneously to import modules from a relative path. | ||
relative-js-path="../js/folder/here" # (string) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Quickstart | ||
|
||
Demo Gen takes a bit of configuration if you don't already have your own Diplomat library set up. | ||
|
||
For that reason, we have a [quickstart repository](https://github.com/rust-diplomat/demo-gen-quickstart). You can also follow along in your own library if you'd like. | ||
|
||
## Requirements | ||
|
||
You'll need to clone [the repository](https://github.com/rust-diplomat/demo-gen-quickstart). | ||
|
||
You'll need to have Rust, Cargo, and the `wasm32-unknown-unknown` target installed: | ||
|
||
```sh | ||
rustup target add wasm32-unknown-unknown | ||
``` | ||
|
||
You'll also need `Node` and `npm` [installed](https://nodejs.org/en/download/package-manager), as Diplomat generates JS code in modules that is easier for Node to parse as a package. | ||
|
||
## Getting Started | ||
|
||
You just need to run (in the repository folder): | ||
|
||
```sh | ||
cargo build -p adder_bindings --target wasm32-unknown-unknown | ||
cargo run -p generator | ||
cp target/wasm32-unknown-unknown/debug/adder_bindings.wasm adder_bindings/demo | ||
``` | ||
|
||
You'll notice the `demo` folder now has a `demo_gen` folder, which is full of JS and rendering files. We can view our results in an HTTP server: | ||
|
||
```sh | ||
npm -C adder_bindings/demo install | ||
npm -C adder_bindings/demo run start | ||
``` | ||
|
||
If you open the server, you should see a webpage listing `AddResult.getAddStr` with a link. If you click the link, you should see something like: | ||
|
||
![A search bar for the web server, with the added URL /demo_gen/rendering/template.html?func=AddResult.getAddStr. Displayed on the webpage is "AddResult.getAddStr" in large text. Below are two inputs: one labelled "left" that has a value of 1, and one labelled "right" that has a value of 2. Below is a submit button. There is output below the button, with the label "Output" and a value of 3.](../../images/demo_output.png) | ||
|
||
And that's it! Let's talk about what each step means. | ||
|
||
# What We Just Accomplished | ||
|
||
When you clone the repository, you'll notice three packages. | ||
|
||
## The Library | ||
|
||
`basic_adder` is our Rust library that we want to make examples of. It only has one function: | ||
|
||
```rs | ||
pub fn add(left: u64, right: u64) -> u64 { | ||
left + right | ||
} | ||
``` | ||
|
||
Everything else we build will be based on this. | ||
|
||
## The Bindings | ||
|
||
`adder_bindings` is our [Diplomat bridge](../basics.md). When we want to make our library accessible in other languages, we need to run `diplomat-tool` on these bindings. | ||
|
||
We build these bundings with | ||
|
||
```sh | ||
cargo build -p adder_bindings --target wasm32-unknown-unknown | ||
``` | ||
|
||
`--target wasm32-unknown-unknown` tells `cargo` to build a `.wasm` file. | ||
|
||
For more on how you can explicitly configure your bindings to work better in demos, see [the chapter on attributes](attributes.md). | ||
|
||
## The Generator | ||
|
||
`generator` is our wrapper for calling `diplomat-tool`. demo_gen is still in progress, and so we need a wayt to use the latest version of `diplomat-tool` to use this experimental backend. `generator` may be removed in future versions of this tutorial. | ||
|
||
We run the generator with | ||
|
||
```sh | ||
cargo run -p generator | ||
``` | ||
|
||
And it performs the equivalent of: | ||
|
||
```sh | ||
diplomat-tool demo_gen adder_bindings/demo/demo_gen --entry adder_bindings/src/lib.rs | ||
``` | ||
|
||
demo_gen will automatically generate JS bindings by default, along with a bunch of other files to help you get started as easily as possible. If you have JS bindings elsewhere, or a different file structure from the Quickstart repository, you can configure how demo_gen works with a library config file. See [the section on markup generation for more](markup.md). | ||
|
||
## The Web Demo | ||
|
||
demo_gen is designed to work with most Diplomat libraries with minimal configuration. However, we currently don't provide *everything* that you'll need to instantly see a demo | ||
|
||
The minimum requirement is at least a web server to load JS modules. | ||
|
||
This is why we have you run | ||
|
||
```sh | ||
npm -C adder_bindings/demo install | ||
npm -C adder_bindings/demo run start | ||
``` | ||
|
||
See the chapter on [the renderer](./renderer.md) for more. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Configuring the Default Renderer | ||
|
||
demo_gen comes bundled with an HTML renderer to make getting started with creating demo_gen output to be as fast as possible. The default renderer is also designed to be highly customizable for your own preferences or front ends. | ||
|
||
The front end renderer uses [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components), which are natively supported by most browsers. For this reason, it should be very portable into other front end systems like Svelte or React. However, if you're dead set on a solution that works even *better* for your front end of choice, you should read [making your own renderer](./custom_renderer.md). | ||
|
||
For more on how the default renderer works, you can read our [design doc](https://github.com/rust-diplomat/diplomat/blob/main/docs/design_doc.md). | ||
|
||
Regardless, let's discuss some ways you can customize the default renderer to your liking. | ||
|
||
## template.html | ||
|
||
`rendering/template.html` represents a list of templates that demo_gen's default renderer will use | ||
|
||
demo_gen will automatically generate `template.html` in the rendering folder. There is nothing that ties `template.html` to this folder specifically however; you can copy, modify, and link to a changed `template.html` file for custom HTML, JS, and CSS. | ||
|
||
For instance, this is one template we've overridden in the ICU4X repo to take advantage of Bootstrap: | ||
|
||
```html | ||
<template id="terminus"> | ||
<link rel="stylesheet" href="dist/index.css"/> | ||
<div class="vstack gap-2"> | ||
<h1><slot name="func-name"></slot></h1> | ||
<slot name="parameters"></slot> | ||
<button type="submit" class="btn btn-primary" data-submit>Submit</button> | ||
<div class="card"> | ||
<div class="card-header">Output</div> | ||
<div class="card-body"> | ||
<p><slot name="output">Output Shown Here</slot></p> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
``` | ||
|
||
For `<template>` tags, we hook into events by looking for `data-*` attributes, which have some of the following properties: | ||
|
||
- `data-submit` tells the attached element to await a press before attempting to run demo_gen code (only works for the `#terminus` tag). | ||
- `data-oninput` tells the attached element to listen for the `oninput` event and save the user's input on this element for submission. | ||
|
||
If you're on the [quickstart](quickstart.md) repository, you might try copying `template.html` out of the rendering folder and modifying it yourself to include your own stylesheets. | ||
|
||
> [!NOTE] | ||
> Because the renderer uses the Web Components API, stylesheets need to be linked inside of each `<template>` tag. | ||
## runtime.mjs | ||
|
||
This is simply a wrapper for the underlying `rendering/rendering.mjs`, which contains most of the logic for taking `<template>` tags and transforming them into | ||
|
||
The expected end result of `runtime.mjs` is to create a `TerminusRender` object from `rendering.mjs`, and append it to the HTML. | ||
|
||
If you are interested in overriding the underlying Javascript more thoroughly, reading the documentation [on writing your own custom renderer](custom_renderer.md) is recommended. Otherwise, you will mostly be interested in overwriting the `evaluateExternal` parameter, which looks something like this: | ||
|
||
```js | ||
(param, updateParamEvent) => { | ||
console.error(`Unrecognized parameter type ${param}`); | ||
} | ||
``` | ||
|
||
If you've flagged anything with the [external](attributes.md#diplomatdemoexternal) attribute, you can check for parameters that Diplomat cannot evaluate on its own and provide these yourself with the `updateParamEvent(updatedParamValue)` callback, containing the value of the parameter that is required. | ||
|
||
> [!TIP] | ||
> `evaluateExternal` is only called once on creation, so if you're planning on updating a param more than once, you should save a dictionary of `updateParamEvent` callbacks somewhere for future reference. | ||
For example, in the ICU4X demo, we look for the DataProvider parameter and provide it from a compiled set of data: | ||
|
||
```js | ||
let dataProvider = DataProvider.compiled(); | ||
let evaluateExternal = (param, updateParamEvent) => { | ||
if (parameter.type === "DataProvider") { | ||
updateParamEvent(dataProvider); | ||
} else { | ||
console.error(`Unrecognized parameter type ${param}`); | ||
} | ||
}; | ||
``` | ||
|
||
## index.html | ||
|
||
demo_gen currently doesn't provide an `index.html` file for you, as even with the default renderer your file structure can vary wildly. It is up to the user to provide their own additional `.html` files. | ||
|
||
If you're looking to get into output right away: you can access any function from the default renderer by opening `template.html` from your webserver with the URL `/renderer/template.html?func=TypeName.functionName`. | ||
|
||
Here's the current script that [the quickstart](quickstart.md) has to list all possible function names: | ||
|
||
```js | ||
import { RenderInfo } from "./demo_gen/index.mjs"; | ||
|
||
Object.values(RenderInfo.termini).forEach((t) => { | ||
let a = document.createElement("li"); | ||
a.innerHTML = `<a href="demo_gen/rendering/template.html?func=${t.funcName}">${t.funcName}</a>`; | ||
document.getElementById("links").appendChild(a); | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters