···11+# Typst plugin for [numbat](https://numbat.dev)
22+33+Built using [typst-wasm](https://github.com/sjfhsjfh/typst-wasm).
44+55+First compile the rust code to WASM using:
66+77+```sh
88+cargo build --target wasm32-unknown-unknown --release
99+```
1010+1111+Then install the Typst package locally using:
1212+1313+```sh
1414+tlp init
1515+```
1616+1717+Then just load the functions like:
1818+1919+```typ
2020+#import "@local/numbat:0.1.0": to_uppercase
2121+```
+66
src/lib.rs
···11+use std::str;
22+use typst_wasm_protocol::wasm_export;
33+44+// Simple string operation function
55+#[wasm_export]
66+fn to_uppercase(input: &[u8]) -> Vec<u8> {
77+ let input_str = match str::from_utf8(input) {
88+ Ok(s) => s,
99+ Err(_) => return b"Invalid UTF-8 input".to_vec(),
1010+ };
1111+1212+ input_str.to_uppercase().into_bytes()
1313+}
1414+1515+// Using custom export name
1616+#[wasm_export(export_rename = "count_chars")]
1717+fn count_characters(input: &[u8]) -> Vec<u8> {
1818+ let input_str = match str::from_utf8(input) {
1919+ Ok(s) => s,
2020+ Err(_) => return b"Invalid UTF-8 input".to_vec(),
2121+ };
2222+2323+ format!("Character count: {}", input_str.chars().count()).into_bytes()
2424+}
2525+2626+// Function returning Result type
2727+#[wasm_export]
2828+fn divide_numbers(input: &[u8]) -> Result<String, String> {
2929+ let input_str = str::from_utf8(input).map_err(|e| format!("UTF-8 error: {}", e))?;
3030+3131+ let numbers: Vec<&str> = input_str.split(',').collect();
3232+ if numbers.len() != 2 {
3333+ return Err("Expected two comma-separated numbers".to_string());
3434+ }
3535+3636+ let a: f64 = numbers[0]
3737+ .trim()
3838+ .parse()
3939+ .map_err(|_| "First value is not a valid number".to_string())?;
4040+ let b: f64 = numbers[1]
4141+ .trim()
4242+ .parse()
4343+ .map_err(|_| "Second value is not a valid number".to_string())?;
4444+4545+ if b == 0.0 {
4646+ return Err("Cannot divide by zero".to_string());
4747+ }
4848+4949+ let result = a / b;
5050+ Ok(format!("Result: {:.2}", result))
5151+}
5252+5353+// Function returning Result<String, String> type
5454+#[wasm_export]
5555+fn validate_email(input: &[u8]) -> Result<String, String> {
5656+ let email = str::from_utf8(input)
5757+ .map_err(|e| format!("UTF-8 error: {}", e))?
5858+ .trim();
5959+6060+ // Simple email validation
6161+ if !email.contains('@') || !email.contains('.') {
6262+ return Err("Invalid email format".to_string());
6363+ }
6464+6565+ Ok("Email is valid".to_string())
6666+}
+3
src/lib.typ
···11+// Should compile numbat_typst.wasm first
22+// using:
33+#import plugin("../target/wasm32-unknown-unknown/release/numbat_typst.wasm"): to_uppercase, count_chars, divide_numbers, validate_email
+27
tests/examples.typ
···11+#import "@local/numbat:0.1.0": to_uppercase, count_chars, divide_numbers, validate_email
22+33+= Tests
44+55+Hi there!
66+77+#let input = "Hello, Typst!"
88+99+// Call the to_uppercase function
1010+#let uppercase = str(to_uppercase(bytes(input)))
1111+Original: #input\
1212+Uppercase: #uppercase
1313+1414+// Call the count_chars function (note this is exported with a custom name)
1515+#let char_count = str(count_chars(bytes(input)))
1616+#char_count
1717+1818+// Call a function that returns a Result type
1919+#let division_result = str(divide_numbers(bytes("10,2")))
2020+#division_result
2121+2222+// Handle potential errors, uncomment to see the error handling in action
2323+// #let division_error = divide_numbers(bytes("10,0"))
2424+2525+// Use a function with Result<String, String> type
2626+#let email_valid = str(validate_email(bytes("user@example.com")))
2727+Email validation: #email_valid