GitHub CI crates.io License

Haystack library

Implementation of the Haystack 4 spec in the Rust programming language.

The library covers extensive parts of the specification, and it uses the cargo features to allow opt-in on features such as decoders, filter, units, timezone, and the C FFI API.

The library requires the allocator feature and the standard library, but it can be compiled to WASM as a non OS target.

Building

Using cargo cargo build creates a debug version, cargo build --release creates a release version.

Specialize builds for each feature set can be compiled as cargo build --release --no-default-features --features "encoders, zinc", which will compile only the core types and the zinc encoding modules, resulting in a small binary (12KB on Windows x86-64)

Testing

Run unit and integration tests with cargo test

Features

Types

The library fundamental type is Value. It can hold any of the Haystack supported data-types.

Scalar types

Create a Str Value from a &str ```rust use libhaystack::val::*; // Creates a Str Value let value = Value::from("Hello");

// Get a std::String from the value assert!(String::try_form(value).expect("String"), "Hello"); ```

Create a Number Value with a unit ```rust use libhaystack::val::; use libhaystack::units::;

// Creates a Number Value using the Value helper function let a = Value::makenumberunit(42, get_unit("m³"));

// Creates the Number scalar let b = Number::makewithunit(100.0, "m³".into());

// Add numbers with units asserteq!(Number::tryform(a).expect("Number") + b, Number::makewithunit(142.0, get_unit("m³"))); ```

Complex types

Create a Haystack Dict ```rust use libhaystack::val::*;

// Create the Dict type let dict = Value::from(dict! { "site" => Value::makemarker(), "name" => Value::makestr("Foo") });

assert!(dict.has("site")); asserteq!(dictvalue.get_str("name"), Some(&"Foo".into()));

// Wrap the type as a Value let value: Value = dict.into(); assert!(value.is_dict()); ```

Filter

A Haystack 4 compliant filter parser and evaluator is provided that uses the ontology definitions from Project Haystack.

```rust use libhaystack::dict; use libhaystack::val::; use libhaystack::filter::;

// Parse the filter from a string let filter = Filter::try_from(r#"site and dis=="Test""#).expect("Filter");

// Define a Dict to apply the filter against let dict = dict!{"site" => Value::make_marker(), "dis" => Value::from("Test")};

// Verify that the filter matched the Dict assert_eq!(dict.filter(&filter), true);

// Filter using advanced ontology query let filter = Filter::try_from(r#"^geoPlace and dis=="Test""#).expect("Filter");

// Sites are geoPlaces let dict = dict!{"site" => Value::make_marker(), "dis" => Value::from("Test")};

// Verify that the filter matched the Dict assert_eq!(dict.filter(&filter), true);

```

Encoding

The library provides support for JSON and Zinc encoding.

JSON is provided through the excellent Serde library, and Zinc is provided as a hand tuned decoder and encoder, with a performance oriented streaming lazy Grid rows parser.

rust use libhaystack::val::*; // Decode a `Str` haystack `Value` from JSON encoding let value: Value = serde_json::from_str("'hello'").unwrap();

rust use libhaystack::val::*; use libhaystack::encoding::zinc::*; // Decode a `Number` haystack `Value` from Zinc encoding let value: Value = decode::from_str("42s").unwrap();

C API

This library exposes a C based API that allows it to be consumed by other programming languages with a C FFI. The header file generation is done using cbindgen

Building the header file: cbindgen --lang c -q --crate libhaystack --output src/c_api/libhaystack.h

Webassembly support

By leveraging the C API, the function exposed can be called in browsers, Node.js, or Deno.

For this wasm-pack is used to generate the wasm binary file, the JS wrapper for initialization, and a typescript file with the API definitions.

wasm-pack build --out-dir wasm --target web