···1-# Typst plugin for [numbat](https://numbat.dev)
00023Built using [typst-wasm](https://github.com/sjfhsjfh/typst-wasm).
45First compile the rust code to WASM using:
67```sh
08cargo build --target wasm32-unknown-unknown --release
9```
10···17Then just load the functions like:
1819```typ
20-#import "@local/numbat:0.1.0": to_uppercase
21```
···1+# Evaltor: Typst plugin for unit-aware calculations
2+3+I initially wanted to use [numbat](https://github.com/sharkdp/numbat) but since it requires access to filesystem I ended up choosing [fend](https://printfn.github.io/fend) as the backend. Maybe one day I will try to implement InMemory module importer where all the nbt files are imported at compilation.
4+56Built using [typst-wasm](https://github.com/sjfhsjfh/typst-wasm).
78First compile the rust code to WASM using:
910```sh
11+rustup target add wasm32-unknown-unknown
12cargo build --target wasm32-unknown-unknown --release
13```
14···21Then just load the functions like:
2223```typ
24+#import "@local/evaltor:0.1.0": print_eval
25```
+14-60
src/lib.rs
···1use std::str;
2use typst_wasm_protocol::wasm_export;
34-// 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}
···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
00000
···1// Should compile numbat_typst.wasm first
2// using:
3+#import plugin("../target/wasm32-unknown-unknown/release/evaltor.wasm"):fend_evaluate
4+5+#let print_eval(input) = {
6+ let output = str(fend_evaluate(bytes(input)))
7+ [#input = #output]
8+}
+25-19
tests/examples.typ
···1-#import "@local/numbat:0.1.0": to_uppercase, count_chars, divide_numbers, validate_email
23-= Tests
000045-Hi there!
67-#let input = "Hello, Typst!"
89-// Call the to_uppercase function
10-#let uppercase = str(to_uppercase(bytes(input)))
11-Original: #input\
12-Uppercase: #uppercase
1314-// 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
1718-// Call a function that returns a Result type
19-#let division_result = str(divide_numbers(bytes("10,2")))
20-#division_result
2122-// Handle potential errors, uncomment to see the error handling in action
23-// #let division_error = divide_numbers(bytes("10,0"))
02425-// Use a function with Result<String, String> type
26-#let email_valid = str(validate_email(bytes("user@example.com")))
27-Email validation: #email_valid
00000000
···1+#import "@local/evaltor:0.1.0": print_eval
23+#show heading: it => {
4+ it.body
5+ v(1pt)
6+}
7+#set text(size:16pt)
89+= `print_eval` with `fend` backend
1011+It will print the input along with its result, calculated using `fend` backend, which is unit-aware.
1213+Some examples:
0001415+#print_eval("2 km + 30 m")
001617+#print_eval("1 ft to cm")
001819+#print_eval("5'7\" to cm")
20+21+#print_eval("cos (pi/4) + i * (sin (pi/4))")
2223+#print_eval("0b1001 + 3")
24+25+#print_eval("0xffff to decimal")
26+27+#print_eval("100 C to F")
28+29+#print_eval("temperature = 30 °C; temperature to °F")
30+31+#print_eval("(10um)/(0.334cm) + (0.6mm)/(35.28cm) + (1mm)/(47.57cm) + (0.4mm)/(35.28cm) + (2um)/(3.56cm) to percent")
32+33+*Currently, random numbers are not available.*