docs.sheetjs.com/docz/docs/03-demos/42-engines/21-boa.md

4.1 KiB

title pagination_prev pagination_next
Rust + Boa demos/bigdata/index solutions/input

import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';

:::warning pass

In a production application, it is strongly recommended to use a binding for a more performant engine like v8

:::

Boa is a pure-Rust JavaScript engine.

The SheetJS Standalone scripts can be parsed and evaluated in a Boa context.

Integration Details

Initialize Engine

A JS context can be constructed in one line:

use boa_engine::Context;

/* initialize */
let context = &mut Context::default();

The following helper function evaluates strings as JS code:

use std::string::String;
use boa_engine::{Context, Source, JsError};

/* simple wrapper to evaluate code snippets */
fn eval_code(c: &mut Context, code: &str) -> Result<String, JsError> {
  let src = Source::from_bytes(code);
  match c.eval(src) {
    Ok(res) => { return Ok(res.to_string(c).unwrap().to_std_string_escaped()); }
    Err(e) => { return Err(e); }
  };
}

Load SheetJS Scripts

Boa provides a special helper to read source code from a path:

use std::path::Path;
use std::string::String;
use boa_engine::{Context, Source, JsError};

/* simple wrapper to evaluate an entire script file */
fn eval_file(c: &mut Context, path: &str) -> Result<String, JsError> {
  let src = Source::from_filepath(Path::new(path)).unwrap();
  match c.eval(src) {
    Ok(res) => { return Ok(res.to_string(c).unwrap().to_std_string_escaped()); }
    Err(e) => { return Err(e); }
  };
}

// ...
  /* load library */
  match eval_file(context, "./xlsx.full.min.js") {
    Ok(_res) => {}
    Err(e) => { return eprintln!("Uncaught {e}"); }
  }

To confirm the library is loaded, XLSX.version can be inspected:

  /* get version string */
  match eval_code(context, "XLSX.version") {
    Ok(res) => { println!( "SheetJS library version {}", res); }
    Err(e) => { return eprintln!("Uncaught {e}"); }
  }

Reading Files

Boa supports ArrayBuffer natively. This snippet reads data from a file into Vec<u8> and stores the data as an ArrayBuffer in global scope:

  /* read file */
  let data: Vec<u8> = fs::read("pres.xlsx").unwrap();
  let array: JsArrayBuffer = JsArrayBuffer::from_byte_block(data, context).unwrap();
  let attrs = Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE;
  context.register_global_property("buf", array, attrs);

  /* parse with SheetJS */
  match eval_code(context, "void (globalThis.wb = XLSX.read(buf))") {
    Ok(_res) => { }
    Err(e) => { return eprintln!("Uncaught {e}"); }
  }

wb will be a variable in the JS environment that can be inspected using the various SheetJS API functions.

Complete Example

:::note

This demo was tested in the following deployments:

Architecture Date
darwin-x64 2023-08-31
darwin-arm 2023-07-05
win10-x64 2023-08-31
win11-arm 2023-09-26
linux-x64 2023-10-11
linux-arm 2023-08-30

:::

  1. Create a new project:
cargo new sheetjs-boa
cd sheetjs-boa
cargo run
  1. Add the boa_engine crate:
cargo add boa_engine
  1. Download the SheetJS Standalone script and move to the project directory:
  • xlsx.full.min.js

{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js}

  1. Download main.rs and replace src/main.rs:
curl -L -o src/main.rs https://docs.sheetjs.com/boa/main.rs
  1. Download the test file and run:
curl -LO https://sheetjs.com/pres.xlsx
cargo run --release

After a short wait, the contents will be displayed in CSV form.

:::caution pass

The default debug build is not optimized and can elicit stack overflow errors. It is strongly encouraged to use --release when possible.

:::