diff --git a/bindings/wasm/.gitignore b/bindings/wasm/.gitignore index c8b241f..e420ee4 100644 --- a/bindings/wasm/.gitignore +++ b/bindings/wasm/.gitignore @@ -1 +1 @@ -target/* \ No newline at end of file +target/* diff --git a/bindings/wasm/Makefile b/bindings/wasm/Makefile index 3f1390d..0b251de 100644 --- a/bindings/wasm/Makefile +++ b/bindings/wasm/Makefile @@ -1,6 +1,8 @@ all: wasm-pack build --target web --scope ironcalc cp README.pkg.md pkg/README.md + tsc types.ts --target esnext --module esnext + python fix_types.py lint: cargo check @@ -10,5 +12,6 @@ lint: clean: cargo clean rm -rf pkg + rm -f types.js -.PHONY: all web lint \ No newline at end of file +.PHONY: all lint clean diff --git a/bindings/wasm/fix_types.py b/bindings/wasm/fix_types.py new file mode 100644 index 0000000..0cb6de1 --- /dev/null +++ b/bindings/wasm/fix_types.py @@ -0,0 +1,97 @@ +# 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 {TokenType[]} +*/ +export function getTokens(formula: string): TokenType[]; +""".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() + +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) + with open("types.ts") as f: + types_str = f.read() + header_types = "{}\n\n{}".format(header, types_str) + text = text.replace(header, header_types) + return text + + + +if __name__ == "__main__": + types_file = "pkg/wasm.d.ts" + with open(types_file) as f: + text = f.read() + text = fix_types(text) + with open(types_file, "wb") as f: + f.write(bytes(text, "utf8")) + + js_file = "pkg/wasm.js" + with open("types.js") as f: + text_js = f.read() + with open(js_file) as f: + text = f.read() + + with open(js_file, "wb") as f: + f.write(bytes("{}\n{}".format(text_js, text), "utf8")) + + + diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 2a0185e..47c256c 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -3,11 +3,23 @@ use wasm_bindgen::{ JsValue, }; -use ironcalc_base::{expressions::types::Area, UserModel as BaseModel}; +use ironcalc_base::{ + expressions::{lexer::util::get_tokens as tokenizer, types::Area}, + types::CellType, + UserModel as BaseModel, +}; fn to_js_error(error: String) -> JsError { JsError::new(&error.to_string()) } + +/// 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")] +pub fn get_tokens(formula: &str) -> Result { + let tokens = tokenizer(formula); + serde_wasm_bindgen::to_value(&tokens).map_err(JsError::from) +} #[wasm_bindgen] pub struct Model { model: BaseModel, @@ -21,6 +33,11 @@ impl Model { Ok(Model { model }) } + pub fn from_bytes(bytes: &[u8]) -> Result { + let model = BaseModel::from_bytes(bytes).map_err(to_js_error)?; + Ok(Model { model }) + } + pub fn undo(&mut self) -> Result<(), JsError> { self.model.undo().map_err(to_js_error) } @@ -247,6 +264,24 @@ impl Model { .map(|x| serde_wasm_bindgen::to_value(&x).unwrap()) } + #[wasm_bindgen(js_name = "getCellType")] + pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result { + Ok( + match self + .model + .get_cell_type(sheet, row, column) + .map_err(to_js_error)? + { + CellType::Number => 1, + CellType::Text => 2, + CellType::LogicalValue => 4, + CellType::ErrorValue => 16, + CellType::Array => 64, + CellType::CompoundData => 128, + }, + ) + } + #[wasm_bindgen(js_name = "getWorksheetsProperties")] pub fn get_worksheets_properties(&self) -> JsValue { serde_wasm_bindgen::to_value(&self.model.get_worksheets_properties()).unwrap() diff --git a/bindings/wasm/types.ts b/bindings/wasm/types.ts new file mode 100644 index 0000000..65b4827 --- /dev/null +++ b/bindings/wasm/types.ts @@ -0,0 +1,194 @@ +export interface Area { + sheet: number; + row: number; + column: number; + width: number; + height: number; +} + +type ErrorType = + | "REF" + | "NAME" + | "VALUE" + | "DIV" + | "NA" + | "NUM" + | "ERROR" + | "NIMPL" + | "SPILL" + | "CALC" + | "CIRC"; + +type OpCompareType = + | "LessThan" + | "GreaterThan" + | "Equal" + | "LessOrEqualThan" + | "GreaterOrEqualThan" + | "NonEqual"; + +type OpSumType = "Add" | "Minus"; + +type OpProductType = "Times" | "Divide"; + +interface ReferenceType { + sheet: string | null; + row: number; + column: number; + absolute_column: boolean; + absolute_row: boolean; +} + +interface ParsedReferenceType { + column: number; + row: number; + absolute_column: boolean; + absolute_row: boolean; +} + +interface Reference { + Reference: ReferenceType; +} + +interface Range { + Range: { + sheet: string | null; + left: ParsedReferenceType; + right: ParsedReferenceType; + }; +} + +export type TokenType = + | "Illegal" + | "Eof" + | { Ident: string } + | { String: string } + | { Boolean: boolean } + | { Number: number } + | { ERROR: ErrorType } + | { COMPARE: OpCompareType } + | { SUM: OpSumType } + | { PRODUCT: OpProductType } + | "POWER" + | "LPAREN" + | "RPAREN" + | "COLON" + | "SEMICOLON" + | "LBRACKET" + | "RBRACKET" + | "LBRACE" + | "RBRACE" + | "COMMA" + | "BANG" + | "PERCENT" + | "AND" + | Reference + | Range; + +export interface MarkedToken { + token: TokenType; + start: number; + end: number; +} + +export interface WorksheetProperties { + name: string; + color: string; + sheet_id: number; +} + +interface CellStyleFill { + pattern_type: string; + fg_color?: string; + bg_color?: string; +} + +interface CellStyleFont { + u: boolean; + b: boolean; + i: boolean; + strike: boolean; + sz: number; + color: string; + name: string; + family: number; + scheme: string; +} + +export enum BorderType { + BorderAll, + BorderInner, + BorderCenterH, + BorderCenterV, + BorderOuter, + BorderNone, + BorderTop, + BorderRight, + BorderBottom, + BorderLeft, + None, +} + +export interface BorderOptions { + color: string; + style: BorderStyle; + border: BorderType; +} + +export enum BorderStyle { + Thin = "thin", + Medium = "medium", + Thick = "thick", + Dashed = "dashed", + Dotted = "dotted", + Double = "double", + None = "none", +} + +interface BorderItem { + style: string; + color: string; +} + +interface CellStyleBorder { + diagonal_up?: boolean; + diagonal_down?: boolean; + left: BorderItem; + right: BorderItem; + top: BorderItem; + bottom: BorderItem; + diagonal: BorderItem; +} + +export type VerticalAlignment = + | "bottom" + | "center" + | "distributed" + | "justify" + | "top"; + +export type HorizontalAlignment = + | "left" + | "center" + | "right" + | "general" + | "centerContinuous" + | "distributed" + | "fill" + | "justify"; + +interface Alignment { + horizontal: HorizontalAlignment; + vertical: VerticalAlignment; + wrap_text: boolean; +} + +export interface CellStyle { + read_only: boolean; + quote_prefix: boolean; + fill: CellStyleFill; + font: CellStyleFont; + border: CellStyleBorder; + num_fmt: string; + alignment?: Alignment; +}