From 5ca50f15d74987d4f3aaf9f1a59cd51ec9cc4f65 Mon Sep 17 00:00:00 2001 From: Brian Hung Date: Thu, 12 Jun 2025 16:23:15 -0700 Subject: [PATCH] feat: use wasm-bindgen unchecked_return_type --- Cargo.lock | 32 +++-- bindings/wasm/Cargo.toml | 2 +- bindings/wasm/fix_types.py | 232 ++----------------------------------- bindings/wasm/src/lib.rs | 46 +++++--- 4 files changed, 66 insertions(+), 246 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbcdd99..bca3abd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -872,6 +872,12 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + [[package]] name = "ryu" version = "1.0.17" @@ -1081,23 +1087,24 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -1118,9 +1125,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1128,9 +1135,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -1141,9 +1148,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 2315210..30455a4 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib"] # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#multiple-locations ironcalc_base = { path = "../../base", version = "0.5", features = ["use_regex_lite"] } serde = { version = "1.0", features = ["derive"] } -wasm-bindgen = "0.2.92" +wasm-bindgen = "0.2.100" serde-wasm-bindgen = "0.4" [dev-dependencies] diff --git a/bindings/wasm/fix_types.py b/bindings/wasm/fix_types.py index fd466ee..0228598 100644 --- a/bindings/wasm/fix_types.py +++ b/bindings/wasm/fix_types.py @@ -1,230 +1,24 @@ -# Regrettably at the time of writing there is not a perfect way to -# generate the TypeScript types from Rust so we basically fix them manually -# Hopefully this will suffice for our needs and one day will be automatic - header = r""" /* tslint:disable */ /* eslint-disable */ """.strip() -get_tokens_str = r""" -* @returns {any} -*/ -export function getTokens(formula: string): any; -""".strip() - -get_tokens_str_types = r""" -* @returns {MarkedToken[]} -*/ -export function getTokens(formula: string): MarkedToken[]; -""".strip() - -update_style_str = r""" -/** -* @param {any} range -* @param {string} style_path -* @param {string} value -*/ - updateRangeStyle(range: any, style_path: string, value: string): void; -""".strip() - -update_style_str_types = r""" -/** -* @param {Area} range -* @param {string} style_path -* @param {string} value -*/ - updateRangeStyle(range: Area, style_path: string, value: string): void; -""".strip() - -properties = r""" -/** -* @returns {any} -*/ - getWorksheetsProperties(): any; -""".strip() - -properties_types = r""" -/** -* @returns {WorksheetProperties[]} -*/ - getWorksheetsProperties(): WorksheetProperties[]; -""".strip() - -style = r""" -* @returns {any} -*/ - getCellStyle(sheet: number, row: number, column: number): any; -""".strip() - -style_types = r""" -* @returns {CellStyle} -*/ - getCellStyle(sheet: number, row: number, column: number): CellStyle; -""".strip() - -view = r""" -* @returns {any} -*/ - getSelectedView(): any; -""".strip() - -view_types = r""" -* @returns {CellStyle} -*/ - getSelectedView(): SelectedView; -""".strip() - -autofill_rows = r""" -/** -* @param {any} source_area -* @param {number} to_row -*/ - autoFillRows(source_area: any, to_row: number): void; -""" - -autofill_rows_types = r""" -/** -* @param {Area} source_area -* @param {number} to_row -*/ - autoFillRows(source_area: Area, to_row: number): void; -""" - -autofill_columns = r""" -/** -* @param {any} source_area -* @param {number} to_column -*/ - autoFillColumns(source_area: any, to_column: number): void; -""" - -autofill_columns_types = r""" -/** -* @param {Area} source_area -* @param {number} to_column -*/ - autoFillColumns(source_area: Area, to_column: number): void; -""" - -set_cell_style = r""" -/** -* @param {any} styles -*/ - onPasteStyles(styles: any): void; -""" - -set_cell_style_types = r""" -/** -* @param {CellStyle[][]} styles -*/ - onPasteStyles(styles: CellStyle[][]): void; -""" - -set_area_border = r""" -/** -* @param {any} area -* @param {any} border_area -*/ - setAreaWithBorder(area: any, border_area: any): void; -""" - -set_area_border_types = r""" -/** -* @param {Area} area -* @param {BorderArea} border_area -*/ - setAreaWithBorder(area: Area, border_area: BorderArea): void; -""" - -paste_csv_string = r""" -/** -* @param {any} area -* @param {string} csv -*/ - pasteCsvText(area: any, csv: string): void; -""" - -paste_csv_string_types = r""" -/** -* @param {Area} area -* @param {string} csv -*/ - pasteCsvText(area: Area, csv: string): void; -""" - -clipboard = r""" -/** -* @returns {any} -*/ - copyToClipboard(): any; -""" - -clipboard_types = r""" -/** -* @returns {Clipboard} -*/ - copyToClipboard(): Clipboard; -""" - -paste_from_clipboard = r""" -/** -* @param {number} source_sheet -* @param {any} source_range -* @param {any} clipboard -* @param {boolean} is_cut -*/ - pasteFromClipboard(source_sheet: number, source_range: any, clipboard: any, is_cut: boolean): void; -""" - -paste_from_clipboard_types = r""" -/** -* @param {number} source_sheet -* @param {[number, number, number, number]} source_range -* @param {ClipboardData} clipboard -* @param {boolean} is_cut -*/ - pasteFromClipboard(source_sheet: number, source_range: [number, number, number, number], clipboard: ClipboardData, is_cut: boolean): void; -""" - -defined_name_list = r""" -/** -* @returns {any} -*/ - getDefinedNameList(): any; -""" - -defined_name_list_types = r""" -/** -* @returns {DefinedName[]} -*/ - getDefinedNameList(): DefinedName[]; -""" - -def fix_types(text): - text = text.replace(get_tokens_str, get_tokens_str_types) - text = text.replace(update_style_str, update_style_str_types) - text = text.replace(properties, properties_types) - text = text.replace(style, style_types) - text = text.replace(view, view_types) - text = text.replace(autofill_rows, autofill_rows_types) - text = text.replace(autofill_columns, autofill_columns_types) - text = text.replace(set_cell_style, set_cell_style_types) - text = text.replace(set_area_border, set_area_border_types) - text = text.replace(paste_csv_string, paste_csv_string_types) - text = text.replace(clipboard, clipboard_types) - text = text.replace(paste_from_clipboard, paste_from_clipboard_types) - text = text.replace(defined_name_list, defined_name_list_types) +def fix_types(text: str): with open("types.ts") as f: types_str = f.read() header_types = "{}\n\n{}".format(header, types_str) - text = text.replace(header, header_types) - if text.find("any") != -1: - print("There are 'unfixed' types. Please check.") - exit(1) - return text + text = text.replace(header, header_types) + for line in text.splitlines(): + line = line.lstrip() + # Skip internal methods + if line.startswith("readonly model_"): + continue + if line.find("any") != -1: + print("There are 'unfixed' public types. Please check.") + exit(1) + return text if __name__ == "__main__": types_file = "pkg/wasm.d.ts" @@ -242,6 +36,4 @@ if __name__ == "__main__": with open(js_file, "wb") as f: f.write(bytes("{}\n{}".format(text_js, text), "utf8")) - - - + \ No newline at end of file diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 5767a2e..0c0b6cb 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -16,7 +16,7 @@ fn to_js_error(error: String) -> JsError { /// Return an array with a list of all the tokens from a formula /// This is used by the UI to color them according to a theme. -#[wasm_bindgen(js_name = "getTokens")] +#[wasm_bindgen(js_name = "getTokens", unchecked_return_type = "MarkedToken[]")] pub fn get_tokens(formula: &str) -> Result { let tokens = tokenizer(formula); serde_wasm_bindgen::to_value(&tokens).map_err(JsError::from) @@ -338,7 +338,7 @@ impl Model { #[wasm_bindgen(js_name = "updateRangeStyle")] pub fn update_range_style( &mut self, - range: JsValue, + #[wasm_bindgen(unchecked_param_type = "Area")] range: JsValue, style_path: &str, value: &str, ) -> Result<(), JsError> { @@ -349,7 +349,7 @@ impl Model { .map_err(to_js_error) } - #[wasm_bindgen(js_name = "getCellStyle")] + #[wasm_bindgen(js_name = "getCellStyle", unchecked_return_type = "CellStyle")] pub fn get_cell_style( &mut self, sheet: u32, @@ -365,7 +365,10 @@ impl Model { } #[wasm_bindgen(js_name = "onPasteStyles")] - pub fn on_paste_styles(&mut self, styles: JsValue) -> Result<(), JsError> { + pub fn on_paste_styles( + &mut self, + #[wasm_bindgen(unchecked_param_type = "CellStyle[][]")] styles: JsValue, + ) -> Result<(), JsError> { let styles: &Vec> = &serde_wasm_bindgen::from_value(styles).map_err(|e| to_js_error(e.to_string()))?; self.model.on_paste_styles(styles).map_err(to_js_error) @@ -391,7 +394,10 @@ impl Model { // I don't _think_ serializing to JsValue can't fail // FIXME: Remove this clippy directive - #[wasm_bindgen(js_name = "getWorksheetsProperties")] + #[wasm_bindgen( + js_name = "getWorksheetsProperties", + unchecked_return_type = "WorksheetProperties[]" + )] #[allow(clippy::unwrap_used)] pub fn get_worksheets_properties(&self) -> JsValue { serde_wasm_bindgen::to_value(&self.model.get_worksheets_properties()).unwrap() @@ -410,7 +416,7 @@ impl Model { // I don't _think_ serializing to JsValue can't fail // FIXME: Remove this clippy directive - #[wasm_bindgen(js_name = "getSelectedView")] + #[wasm_bindgen(js_name = "getSelectedView", unchecked_return_type = "SelectedView")] #[allow(clippy::unwrap_used)] pub fn get_selected_view(&self) -> JsValue { serde_wasm_bindgen::to_value(&self.model.get_selected_view()).unwrap() @@ -469,7 +475,11 @@ impl Model { } #[wasm_bindgen(js_name = "autoFillRows")] - pub fn auto_fill_rows(&mut self, source_area: JsValue, to_row: i32) -> Result<(), JsError> { + pub fn auto_fill_rows( + &mut self, + #[wasm_bindgen(unchecked_param_type = "Area")] source_area: JsValue, + to_row: i32, + ) -> Result<(), JsError> { let area: Area = serde_wasm_bindgen::from_value(source_area).map_err(|e| to_js_error(e.to_string()))?; self.model @@ -480,7 +490,7 @@ impl Model { #[wasm_bindgen(js_name = "autoFillColumns")] pub fn auto_fill_columns( &mut self, - source_area: JsValue, + #[wasm_bindgen(unchecked_param_type = "Area")] source_area: JsValue, to_column: i32, ) -> Result<(), JsError> { let area: Area = @@ -561,8 +571,8 @@ impl Model { #[wasm_bindgen(js_name = "setAreaWithBorder")] pub fn set_area_with_border( &mut self, - area: JsValue, - border_area: JsValue, + #[wasm_bindgen(unchecked_param_type = "Area")] area: JsValue, + #[wasm_bindgen(unchecked_param_type = "BorderArea")] border_area: JsValue, ) -> Result<(), JsError> { let range: Area = serde_wasm_bindgen::from_value(area).map_err(|e| to_js_error(e.to_string()))?; @@ -589,7 +599,7 @@ impl Model { self.model.set_name(name); } - #[wasm_bindgen(js_name = "copyToClipboard")] + #[wasm_bindgen(js_name = "copyToClipboard", unchecked_return_type = "Clipboard")] pub fn copy_to_clipboard(&self) -> Result { let data = self .model @@ -603,8 +613,9 @@ impl Model { pub fn paste_from_clipboard( &mut self, source_sheet: u32, + #[wasm_bindgen(unchecked_param_type = "[number, number, number, number]")] source_range: JsValue, - clipboard: JsValue, + #[wasm_bindgen(unchecked_param_type = "ClipboardData")] clipboard: JsValue, is_cut: bool, ) -> Result<(), JsError> { let source_range: (i32, i32, i32, i32) = @@ -617,7 +628,11 @@ impl Model { } #[wasm_bindgen(js_name = "pasteCsvText")] - pub fn paste_csv_string(&mut self, area: JsValue, csv: &str) -> Result<(), JsError> { + pub fn paste_csv_string( + &mut self, + #[wasm_bindgen(unchecked_param_type = "Area")] area: JsValue, + csv: &str, + ) -> Result<(), JsError> { let range: Area = serde_wasm_bindgen::from_value(area).map_err(|e| to_js_error(e.to_string()))?; self.model @@ -625,7 +640,10 @@ impl Model { .map_err(|e| to_js_error(e.to_string())) } - #[wasm_bindgen(js_name = "getDefinedNameList")] + #[wasm_bindgen( + js_name = "getDefinedNameList", + unchecked_return_type = "DefinedName[]" + )] pub fn get_defined_name_list(&self) -> Result { let data: Vec = self .model