Set up type-safe call and response
This commit is contained in:
parent
c87ab6da7f
commit
b321c03297
1
Makefile
1
Makefile
|
@ -55,6 +55,7 @@ kifu-pwa:
|
|||
|
||||
kifu-pwa/dev:
|
||||
pushd kifu/ffi/wasm && make && popd
|
||||
pushd kifu/core-types && make && popd
|
||||
pushd kifu/kifu-pwa && make dev
|
||||
|
||||
kifu-pwa/test-server:
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
SOURCES = $(shell find ../kifu-core -name "*.rs")
|
||||
dist/core.d.ts: $(SOURCES)
|
||||
mkdir -p dist
|
||||
typeshare ../kifu-core --lang=typescript --output-file=dist/core.d.ts
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "core-types",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"types": "dist/core.d.ts",
|
||||
"scripts": {
|
||||
"build": "make",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Savanni D'Gerinel <savanni@luminescent-dreams.com>",
|
||||
"license": "GPL-3.0-or-later"
|
||||
}
|
|
@ -172,6 +172,8 @@ name = "kifu-wasm"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kifu-core",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
@ -270,6 +272,17 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.160"
|
||||
|
|
|
@ -10,6 +10,8 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
kifu-core = { path = "../../kifu-core" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde-wasm-bindgen = "0.5.0"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "*"
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
// use serde::{Serialize, Deserialize};
|
||||
use kifu_core::{CoreRequest, CoreResponse};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
|
@ -8,8 +10,11 @@ extern "C" {
|
|||
|
||||
/*
|
||||
#[wasm_bindgen]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CoreRequest(kifu_core::Request);
|
||||
*/
|
||||
|
||||
/*
|
||||
#[wasm_bindgen]
|
||||
impl CoreRequest {
|
||||
#[wasm_bindgen(constructor)]
|
||||
|
@ -22,22 +27,26 @@ impl CoreRequest {
|
|||
/* Somehow uncommenting this code actually causes the module to not load. Maybe a name conflict?
|
||||
* Don't know.
|
||||
*/
|
||||
/*
|
||||
#[wasm_bindgen]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CoreResponse(kifu_core::Response);
|
||||
*/
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
pub struct CoreApp;
|
||||
pub struct CoreApp(kifu_core::CoreApp);
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl CoreApp {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
Self(kifu_core::CoreApp::new())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn dispatch(&self, param: &JsValue) {
|
||||
log(&format!("disptach! {:?}", param));
|
||||
pub async fn dispatch(&self, param: &JsValue) -> JsValue {
|
||||
let request: CoreRequest = serde_wasm_bindgen::from_value(param.clone()).unwrap();
|
||||
serde_wasm_bindgen::to_value(&self.0.dispatch(request).await).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use typeshare::typeshare;
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[typeshare]
|
||||
#[serde(tag = "type", content = "content")]
|
||||
pub enum Request {
|
||||
pub enum CoreRequest {
|
||||
PlayingField,
|
||||
PlayStoneRequest(PlayStoneRequest),
|
||||
}
|
||||
|
@ -15,14 +15,14 @@ pub enum Request {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[typeshare]
|
||||
pub struct PlayStoneRequest {
|
||||
pub column: usize,
|
||||
pub row: usize,
|
||||
pub column: u8,
|
||||
pub row: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[typeshare]
|
||||
#[serde(tag = "type", content = "content")]
|
||||
pub enum Response {
|
||||
pub enum CoreResponse {
|
||||
PlayingFieldView(PlayingFieldView),
|
||||
}
|
||||
|
||||
|
@ -38,19 +38,19 @@ impl CoreApp {
|
|||
Self { state }
|
||||
}
|
||||
|
||||
pub async fn dispatch(&self, request: Request) -> Response {
|
||||
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
||||
match request {
|
||||
Request::PlayingField => {
|
||||
CoreRequest::PlayingField => {
|
||||
let app_state = self.state.read().unwrap();
|
||||
let game = app_state.game.as_ref().unwrap();
|
||||
Response::PlayingFieldView(playing_field(game))
|
||||
CoreResponse::PlayingFieldView(playing_field(game))
|
||||
}
|
||||
Request::PlayStoneRequest(request) => {
|
||||
CoreRequest::PlayStoneRequest(request) => {
|
||||
let mut app_state = self.state.write().unwrap();
|
||||
app_state.place_stone(request);
|
||||
|
||||
let game = app_state.game.as_ref().unwrap();
|
||||
Response::PlayingFieldView(playing_field(game))
|
||||
CoreResponse::PlayingFieldView(playing_field(game))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ impl Board {
|
|||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
|
||||
pub struct Coordinate {
|
||||
pub column: usize,
|
||||
pub row: usize,
|
||||
pub column: u8,
|
||||
pub row: u8,
|
||||
}
|
||||
|
||||
impl Board {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod api;
|
||||
pub use api::{CoreApp, Request, Response};
|
||||
pub use api::{CoreApp, CoreRequest, CoreResponse};
|
||||
|
||||
mod types;
|
||||
pub use types::{BoardError, Color, Size};
|
||||
|
|
|
@ -27,8 +27,8 @@ pub enum Color {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[typeshare]
|
||||
pub struct Size {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub width: u8,
|
||||
pub height: u8,
|
||||
}
|
||||
|
||||
impl Default for Size {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::types::{Color, Size};
|
||||
use crate::{
|
||||
api::{PlayStoneRequest, Request},
|
||||
api::{CoreRequest, PlayStoneRequest},
|
||||
Board, Coordinate,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -36,7 +36,7 @@ impl StoneElement {
|
|||
#[serde(tag = "type", content = "content")]
|
||||
pub enum IntersectionElement {
|
||||
Unplayable,
|
||||
Empty(Request),
|
||||
Empty(CoreRequest),
|
||||
Filled(StoneElement),
|
||||
}
|
||||
|
||||
|
@ -53,10 +53,9 @@ impl BoardElement {
|
|||
.map(|row| {
|
||||
(0..size.width)
|
||||
.map(|column| {
|
||||
IntersectionElement::Empty(Request::PlayStoneRequest(PlayStoneRequest {
|
||||
column,
|
||||
row,
|
||||
}))
|
||||
IntersectionElement::Empty(CoreRequest::PlayStoneRequest(
|
||||
PlayStoneRequest { column, row },
|
||||
))
|
||||
})
|
||||
.collect::<Vec<IntersectionElement>>()
|
||||
})
|
||||
|
@ -86,7 +85,7 @@ impl From<&Board> for BoardElement {
|
|||
liberties: None,
|
||||
color,
|
||||
}),
|
||||
None => IntersectionElement::Empty(Request::PlayStoneRequest(
|
||||
None => IntersectionElement::Empty(CoreRequest::PlayStoneRequest(
|
||||
PlayStoneRequest { column, row },
|
||||
)),
|
||||
})
|
||||
|
|
|
@ -4,3 +4,4 @@ dev:
|
|||
|
||||
test-server:
|
||||
npx http-server ./dist
|
||||
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
Generated by typeshare 1.5.0
|
||||
*/
|
||||
|
||||
export interface PlayStoneRequest {
|
||||
column: number;
|
||||
row: number;
|
||||
}
|
||||
|
||||
export interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export type IntersectionElement =
|
||||
| { type: "Unplayable", content?: undefined }
|
||||
| { type: "Empty", content: Request }
|
||||
| { type: "Filled", content: StoneElement };
|
||||
|
||||
export interface BoardElement {
|
||||
size: Size;
|
||||
spaces: IntersectionElement[];
|
||||
}
|
||||
|
||||
export enum Color {
|
||||
Black = "Black",
|
||||
White = "White",
|
||||
}
|
||||
|
||||
export interface PlayerCardElement {
|
||||
color: Color;
|
||||
name: string;
|
||||
rank: string;
|
||||
clock: string;
|
||||
}
|
||||
|
||||
export interface ChatElement {
|
||||
messages: string[];
|
||||
}
|
||||
|
||||
export interface TextFieldElement {
|
||||
}
|
||||
|
||||
export interface PlayingFieldView {
|
||||
board: BoardElement;
|
||||
player_card_black: PlayerCardElement;
|
||||
player_card_white: PlayerCardElement;
|
||||
chat: ChatElement;
|
||||
message: TextFieldElement;
|
||||
current_player: Color;
|
||||
}
|
||||
|
||||
export interface Jitter {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface StoneElement {
|
||||
color: Color;
|
||||
jitter: Jitter;
|
||||
}
|
||||
|
||||
export type Request =
|
||||
| { type: "PlayingField", content?: undefined }
|
||||
| { type: "PlayStoneRequest", content: PlayStoneRequest };
|
||||
|
||||
export type Response =
|
||||
| { type: "PlayingFieldView", content: PlayingFieldView };
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import init, { CoreApp } from "kifu-wasm";
|
||||
|
||||
const inputField = document.getElementById("input-temp");
|
||||
const fromUnitField = document.getElementById("input-unit");
|
||||
const toUnitField = document.getElementById("output-unit");
|
||||
const outputField = document.getElementById("output-temp");
|
||||
const form = document.getElementById("converter");
|
||||
|
||||
function convertTemp(value, fromUnit, toUnit) {
|
||||
if (fromUnit === "c") {
|
||||
if (toUnit === "f") {
|
||||
return (value * 9) / 5 + 32;
|
||||
} else if (toUnit === "k") {
|
||||
return value + 273.15;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
if (fromUnit === "f") {
|
||||
if (toUnit === "c") {
|
||||
return ((value - 32) * 5) / 9;
|
||||
} else if (toUnit === "k") {
|
||||
return ((value + 459.67) * 5) / 9;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
if (fromUnit === "k") {
|
||||
if (toUnit === "c") {
|
||||
return value - 273.15;
|
||||
} else if (toUnit === "f") {
|
||||
return (value * 9) / 5 - 459.67;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
throw new Error("Invalid unit");
|
||||
}
|
||||
|
||||
form.addEventListener("input", () => {
|
||||
const inputTemp = parseFloat(inputField.value);
|
||||
const fromUnit = fromUnitField.value;
|
||||
const toUnit = toUnitField.value;
|
||||
|
||||
const outputTemp = convertTemp(inputTemp, fromUnit, toUnit);
|
||||
outputField.value =
|
||||
Math.round(outputTemp * 100) / 100 + " " + toUnit.toUpperCase();
|
||||
});
|
||||
|
||||
init().then(async () => {
|
||||
let app = new CoreApp();
|
||||
console.log("app: ", app, CoreApp);
|
||||
await app.dispatch({ type: "PlayingField" });
|
||||
console.log("kifu_wasm successfully initted");
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import init, { CoreApp } from "kifu-wasm";
|
||||
import { CoreResponse, CoreRequest, PlayingFieldView } from "core-types";
|
||||
|
||||
export class CoreApi {
|
||||
core: CoreApp;
|
||||
|
||||
constructor() {
|
||||
let app = new CoreApp();
|
||||
this.core = app;
|
||||
}
|
||||
|
||||
async dispatch(request: CoreRequest): Promise<CoreResponse> {
|
||||
return await this.core.dispatch(request);
|
||||
}
|
||||
|
||||
async playingField(): Promise<PlayingFieldView> {
|
||||
return (await this.dispatch({ type: "PlayingField" })).content;
|
||||
}
|
||||
}
|
||||
|
||||
export const initCore = async (): Promise<CoreApi> => {
|
||||
await init();
|
||||
return new CoreApi();
|
||||
};
|
|
@ -1,10 +1,8 @@
|
|||
import init, { CoreApp } from "kifu-wasm";
|
||||
import { CoreApi, initCore } from "./coreApi";
|
||||
|
||||
const main = async () => {
|
||||
await init();
|
||||
let coreApi = new CoreApp();
|
||||
|
||||
let response = await coreApi.dispatch({ type: "PlayingField" });
|
||||
let coreApi = await initCore();
|
||||
let response = await coreApi.playingField();
|
||||
console.log("playing field response: ", response);
|
||||
};
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ module.exports = {
|
|||
"kifu-bundle": "./src/main.ts",
|
||||
sw: "./src/sw.js",
|
||||
},
|
||||
loader: {
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /.ts$/, use: "ts-loader", exclude: /node_modules/ },
|
||||
{ test: /\.ts$/, use: "ts-loader", exclude: /node_modules/ },
|
||||
{ test: /\.wasm$/, type: "asset/inline" }
|
||||
],
|
||||
},
|
||||
|
@ -19,5 +19,8 @@ module.exports = {
|
|||
{ from: "src/converter.css" }
|
||||
]
|
||||
})
|
||||
]
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.ts'],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,16 @@
|
|||
"name": "tools",
|
||||
"workspaces": [
|
||||
"kifu/ffi/wasm/pkg",
|
||||
"kifu/core-types",
|
||||
"kifu/kifu-pwa"
|
||||
]
|
||||
},
|
||||
"kifu/core-types": {
|
||||
"version": "0.0.1",
|
||||
"license": "GPL-3.0-or-later"
|
||||
},
|
||||
"kifu/ffi/wasm/pkg": {
|
||||
"name": "kifu-wasm",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"kifu/kifu-pwa": {
|
||||
|
@ -616,6 +622,10 @@
|
|||
"webpack": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-types": {
|
||||
"resolved": "kifu/core-types",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"name": "tools",
|
||||
"workspaces": [
|
||||
"kifu/ffi/wasm/pkg",
|
||||
"kifu/core-types",
|
||||
"kifu/kifu-pwa"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue