A typst plugin for unit-aware calculations
typst

Initial example

+209
+2
.gitignore
···
··· 1 + /target 2 + *.pdf
+76
Cargo.lock
···
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "numbat-typst" 7 + version = "0.1.0" 8 + dependencies = [ 9 + "typst-wasm-protocol", 10 + ] 11 + 12 + [[package]] 13 + name = "proc-macro2" 14 + version = "1.0.103" 15 + source = "registry+https://github.com/rust-lang/crates.io-index" 16 + checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 17 + dependencies = [ 18 + "unicode-ident", 19 + ] 20 + 21 + [[package]] 22 + name = "quote" 23 + version = "1.0.42" 24 + source = "registry+https://github.com/rust-lang/crates.io-index" 25 + checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 26 + dependencies = [ 27 + "proc-macro2", 28 + ] 29 + 30 + [[package]] 31 + name = "syn" 32 + version = "2.0.111" 33 + source = "registry+https://github.com/rust-lang/crates.io-index" 34 + checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 35 + dependencies = [ 36 + "proc-macro2", 37 + "quote", 38 + "unicode-ident", 39 + ] 40 + 41 + [[package]] 42 + name = "typst-wasm-macros" 43 + version = "0.0.2" 44 + source = "registry+https://github.com/rust-lang/crates.io-index" 45 + checksum = "0df89cf4e273dca9ca1a971f86d5bc12f7ec79d008fbc11037dbc8f821196316" 46 + dependencies = [ 47 + "proc-macro2", 48 + "quote", 49 + "syn", 50 + "venial", 51 + ] 52 + 53 + [[package]] 54 + name = "typst-wasm-protocol" 55 + version = "0.0.2" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "1e74aabca9c7bb93eb529915add290b6cc11231f3fb36f2c6080007a658699df" 58 + dependencies = [ 59 + "typst-wasm-macros", 60 + ] 61 + 62 + [[package]] 63 + name = "unicode-ident" 64 + version = "1.0.22" 65 + source = "registry+https://github.com/rust-lang/crates.io-index" 66 + checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 67 + 68 + [[package]] 69 + name = "venial" 70 + version = "0.6.1" 71 + source = "registry+https://github.com/rust-lang/crates.io-index" 72 + checksum = "9a42528baceab6c7784446df2a10f4185078c39bf73dc614f154353f1a6b1229" 73 + dependencies = [ 74 + "proc-macro2", 75 + "quote", 76 + ]
+10
Cargo.toml
···
··· 1 + [package] 2 + name = "numbat-typst" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [lib] 7 + crate-type = ["cdylib"] 8 + 9 + [dependencies] 10 + typst-wasm-protocol = "0.0.2"
+21
README.md
···
··· 1 + # Typst plugin for [numbat](https://numbat.dev) 2 + 3 + Built using [typst-wasm](https://github.com/sjfhsjfh/typst-wasm). 4 + 5 + First compile the rust code to WASM using: 6 + 7 + ```sh 8 + cargo build --target wasm32-unknown-unknown --release 9 + ``` 10 + 11 + Then install the Typst package locally using: 12 + 13 + ```sh 14 + tlp init 15 + ``` 16 + 17 + Then just load the functions like: 18 + 19 + ```typ 20 + #import "@local/numbat:0.1.0": to_uppercase 21 + ```
+66
src/lib.rs
···
··· 1 + use std::str; 2 + use typst_wasm_protocol::wasm_export; 3 + 4 + // Simple string operation function 5 + #[wasm_export] 6 + fn to_uppercase(input: &[u8]) -> Vec<u8> { 7 + let input_str = match str::from_utf8(input) { 8 + Ok(s) => s, 9 + Err(_) => return b"Invalid UTF-8 input".to_vec(), 10 + }; 11 + 12 + input_str.to_uppercase().into_bytes() 13 + } 14 + 15 + // Using custom export name 16 + #[wasm_export(export_rename = "count_chars")] 17 + fn count_characters(input: &[u8]) -> Vec<u8> { 18 + let input_str = match str::from_utf8(input) { 19 + Ok(s) => s, 20 + Err(_) => return b"Invalid UTF-8 input".to_vec(), 21 + }; 22 + 23 + format!("Character count: {}", input_str.chars().count()).into_bytes() 24 + } 25 + 26 + // Function returning Result type 27 + #[wasm_export] 28 + fn divide_numbers(input: &[u8]) -> Result<String, String> { 29 + let input_str = str::from_utf8(input).map_err(|e| format!("UTF-8 error: {}", e))?; 30 + 31 + let numbers: Vec<&str> = input_str.split(',').collect(); 32 + if numbers.len() != 2 { 33 + return Err("Expected two comma-separated numbers".to_string()); 34 + } 35 + 36 + let a: f64 = numbers[0] 37 + .trim() 38 + .parse() 39 + .map_err(|_| "First value is not a valid number".to_string())?; 40 + let b: f64 = numbers[1] 41 + .trim() 42 + .parse() 43 + .map_err(|_| "Second value is not a valid number".to_string())?; 44 + 45 + if b == 0.0 { 46 + return Err("Cannot divide by zero".to_string()); 47 + } 48 + 49 + let result = a / b; 50 + Ok(format!("Result: {:.2}", result)) 51 + } 52 + 53 + // Function returning Result<String, String> type 54 + #[wasm_export] 55 + fn validate_email(input: &[u8]) -> Result<String, String> { 56 + let email = str::from_utf8(input) 57 + .map_err(|e| format!("UTF-8 error: {}", e))? 58 + .trim(); 59 + 60 + // Simple email validation 61 + if !email.contains('@') || !email.contains('.') { 62 + return Err("Invalid email format".to_string()); 63 + } 64 + 65 + Ok("Email is valid".to_string()) 66 + }
+3
src/lib.typ
···
··· 1 + // Should compile numbat_typst.wasm first 2 + // using: 3 + #import plugin("../target/wasm32-unknown-unknown/release/numbat_typst.wasm"): to_uppercase, count_chars, divide_numbers, validate_email
+27
tests/examples.typ
···
··· 1 + #import "@local/numbat:0.1.0": to_uppercase, count_chars, divide_numbers, validate_email 2 + 3 + = Tests 4 + 5 + Hi there! 6 + 7 + #let input = "Hello, Typst!" 8 + 9 + // Call the to_uppercase function 10 + #let uppercase = str(to_uppercase(bytes(input))) 11 + Original: #input\ 12 + Uppercase: #uppercase 13 + 14 + // Call the count_chars function (note this is exported with a custom name) 15 + #let char_count = str(count_chars(bytes(input))) 16 + #char_count 17 + 18 + // Call a function that returns a Result type 19 + #let division_result = str(divide_numbers(bytes("10,2"))) 20 + #division_result 21 + 22 + // Handle potential errors, uncomment to see the error handling in action 23 + // #let division_error = divide_numbers(bytes("10,0")) 24 + 25 + // Use a function with Result<String, String> type 26 + #let email_valid = str(validate_email(bytes("user@example.com"))) 27 + Email validation: #email_valid
+4
typst.toml
···
··· 1 + [package] 2 + name = "numbat" 3 + version = "0.1.0" 4 + entrypoint = "src/lib.typ"