px_wsdom_gen/
lib.rs

1use std::{collections::BTreeMap, fmt::Display};
2
3use itertools::Itertools;
4use sha3::Digest;
5pub fn gen<D: Display>(modules: &[D], rpcs: &BTreeMap<String, usize>) -> String {
6    let modules = modules.iter().map(|a| format!("{a}")).collect_vec();
7    const S: &str = r#"type Id = number;
8type Value = unknown;
9type SendMessage = (msg: string) => void;
10
11export function WSDOMConnectWebSocket(wsUrl: string | URL, wsProtocols?: string | string[]): WSDOM {
12	const ws = new WebSocket(wsUrl, wsProtocols);
13	const wsdom = new WSDOM((msg: string) => {
14		ws.send(msg);
15	});
16	ws.onopen = () => {
17		console.debug("WSDOM WebSocket connection open!");
18		console.debug("WebSocket object", ws);
19		console.debug("WSDOM object", wsdom);
20	}
21	ws.onmessage = (msg: MessageEvent<string>) => {
22		wsdom.handleIncomingMessage(msg.data);
23	};
24	ws.onclose = (ev: CloseEvent) => {
25		console.debug("WSDOM WebSocket closed", ev);
26	}
27	ws.onerror = (ev: Event) => {
28		console.warn("WSDOM WebSocket errored", ev);
29	}
30    return wsdom;
31}
32export class WSDOM {
33	public internal: WSDOMCore;
34	constructor(sendMessage: SendMessage) {
35		this.internal = new WSDOMCore(sendMessage);
36	}
37	public handleIncomingMessage(msg: string) {
38		const fn = new Function('_w', msg);
39		fn(this.internal);
40	}
41    #e
42}
43export class WSDOMCore{
44	public sender: SendMessage;
45	private values: Map<Id, { value: Value, error: boolean }>;
46    public callbacks: Map<Id,(value: Value) => void>;
47    private next_value: Id;
48	constructor(sender: SendMessage) {
49		this.sender = sender;
50		this.values = new Map();
51        this.callbacks = new Map();
52        this.next_value = Number.MAX_SAFE_INTEGER;
53	}
54    public allocate = (v: Value): Id => {
55        var i = this.next_value;
56        this.next_value--;
57        this.values.set(i,{value: v, error: false});
58        return i;
59    }
60    public a = this.allocate;
61	public g = (id: Id): Value => {
62		var w = this.values.get(id);
63		if (w?.error) {
64			throw w.value
65		} else {
66			return w?.value
67		}
68	}
69	public s = (id: Id, value: Value) => {
70		this.values.set(id, { value, error: false });
71	}
72	public d = (id: Id) => {
73		this.values.delete(id);
74	}
75	public r = (id: Id, val: Value) => {
76		const valJson = JSON.stringify(val);
77		(this.sender)(`p${id}:${valJson}`);
78	}
79    public rp = (id: Id, val: Value) => {
80        var cb = this.callbacks.get(id);
81        if(cb !== undefined){
82            cb(val)
83        }
84	}
85	public c = (id: Id): {value: Value} | {slot: Id} | undefined  => {
86		var w = this.values.get(id);
87		if(w?.error){
88			return {slot: this.allocate(w.value)};
89		}else{
90			return {value: w?.value}
91		}
92	}
93	public e = (id: Id, value: Value) => {
94		this.values.set(id, { value, error: true })
95	}
96    public x: {[key: string]: Value} = {#x};
97}
98"#;
99    return format!(
100        "{}\n{}",
101        modules
102            .iter()
103            .enumerate()
104            .map(|(i, m)| format!("import * as m{i} from '{m}'"))
105            .join("\n"),
106        S.replace(
107            "#x",
108            &modules
109                .iter()
110                .enumerate()
111                .map(|(i, m)| format!(
112                    "_{} :m{i} as Value",
113                    hex::encode(&sha3::Sha3_256::digest(m.as_bytes()))
114                ))
115                .join(",")
116        )
117        .replace(
118            "#e",
119            &rpcs
120                .iter()
121                .map(|(a, v)| format!(
122                    r#"public {a} = ({}): Promise<Value> => {{
123                        return new Promise((then) => {{
124                            var i = 0;
125                            while(this.internal.callbacks.contains(i))i++;
126                            this.internal.callbacks.set(i,then);
127                            var s = `r{a}:${{i}};{}`;
128                            (this.internal.sender)(s);
129                        }});
130                    }}"#,
131                    (0usize..*v).map(|a| format!("param{a}: Value")).join(","),
132                    (0usize..*v)
133                        .map(|a| format!("${{this.interial.allocate(param{a})}}"))
134                        .join(","),
135                ))
136                .join("\n")
137        )
138    );
139}
140pub fn launch(url: &str, path: &str, rpcs: &BTreeMap<String, usize>) -> String {
141    return format!("import WSDOMConnectWebSocket from '{path}'\nexport const WS = WSDOMConnectToServer('{url}')\n{}",rpcs.iter().map(|(a,_)|format!("export const {a} = WS.{a};")).join(";"));
142}