Skip to content

Commit

Permalink
Replaced the trie-rs package by a new ptrie package (it was giving in…
Browse files Browse the repository at this point in the history
…consistent results). Improve docs, add JS tests with jest
  • Loading branch information
vemonet committed Dec 21, 2023
1 parent 9158193 commit bf186e0
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 118 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ jobs:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
with:
submodules: true
- run: cargo build --all-features
- run: cargo test
env:
Expand Down
9 changes: 9 additions & 0 deletions js/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {Config} from 'jest';

const config: Config = {
preset: 'ts-jest',
verbose: true,
// testEnvironment: 'node',
};

export default config;
9 changes: 7 additions & 2 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
],
"scripts": {
"fmt": "prettier \"**/*.{ts,tsx,js,cjs,json,md,html}\" --ignore-path .gitignore --write",
"test": "wasm-pack build --debug --target nodejs && mocha",
"jest" : "jest",
"test": "npm run build && jest",
"build": "rm -rf pkg pkg-web pkg-node && wasm-pack build --release --target web --out-name web && mv pkg pkg-web && wasm-pack build --release --target nodejs --out-name node && mv pkg pkg-node && node build_package.js && rm -r pkg-web pkg-node",
"start": "http-server ./",
"release": "npm run build && npm publish ./pkg --access public",
Expand All @@ -33,8 +34,12 @@
]
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"http-server": "^14.1.1",
"prettier": "^3.1.0"
"jest": "^29.7.0",
"prettier": "^3.1.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.2"
},
"prettier": {
"trailingComma": "none",
Expand Down
153 changes: 145 additions & 8 deletions js/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use std::collections::HashSet;

use curies::{sources::get_obo_converter, Converter, Record};
use js_sys::Promise;
use curies::{
sources::{
get_bioregistry_converter, get_go_converter, get_monarch_converter, get_obo_converter,
},
Converter, Record,
};
use js_sys::{Array, Promise};
use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::to_value;
use wasm_bindgen::prelude::*;
Expand Down Expand Up @@ -64,11 +69,53 @@ impl ConverterJs {
})
}

#[wasm_bindgen(static_method_of = ConverterJs, js_name = fromPrefixMap)]
pub fn from_prefix_map(prefix_map: String) -> Promise {
future_to_promise(async move {
match Converter::from_prefix_map(&*prefix_map).await {
Ok(converter) => Ok(JsValue::from(ConverterJs { converter })),
Err(e) => Err(JsValue::from_str(&e.to_string())),
}
})
}

#[wasm_bindgen(static_method_of = ConverterJs, js_name = fromJsonld)]
pub fn from_jsonld(jsonld: String) -> Promise {
future_to_promise(async move {
match Converter::from_jsonld(&*jsonld).await {
Ok(converter) => Ok(JsValue::from(ConverterJs { converter })),
Err(e) => Err(JsValue::from_str(&e.to_string())),
}
})
}

#[wasm_bindgen(static_method_of = ConverterJs, js_name = fromExtendedPrefixMap)]
pub fn from_extended_prefix_map(prefix_map: String) -> Promise {
future_to_promise(async move {
match Converter::from_extended_prefix_map(&*prefix_map).await {
Ok(converter) => Ok(JsValue::from(ConverterJs { converter })),
Err(e) => Err(JsValue::from_str(&e.to_string())),
}
})
}

#[wasm_bindgen(js_name = addRecord)]
pub fn add_record(&mut self, record: RecordJs) -> Result<(), JsValue> {
self.converter
.add_record(record.record)
.map(|_| self.converter.build())
.map_err(|e| JsValue::from_str(&e.to_string()))
}

#[wasm_bindgen(js_name = addCurie)]
pub fn add_curie(&mut self, prefix: &str, uri_prefix: &str) -> Result<(), JsValue> {
self.converter
.add_curie(prefix, uri_prefix)
.map_err(|e| JsValue::from_str(&e.to_string()))
}

pub fn chain(&self, converter: &ConverterJs) -> Result<ConverterJs, JsValue> {
Converter::chain(vec![self.converter.clone(), converter.converter.clone()])
.map(|converter| ConverterJs { converter })
.map_err(|e| JsValue::from_str(&e.to_string()))
}

Expand All @@ -84,15 +131,36 @@ impl ConverterJs {
.map_err(|e| JsValue::from_str(&e.to_string()))
}

#[wasm_bindgen(js_name = expandList)]
pub fn expand_list(&self, curies: JsValue) -> Result<JsValue, JsValue> {
let curies_vec: Vec<String> = serde_wasm_bindgen::from_value(curies)
.map_err(|e| JsValue::from_str(&format!("Error converting CURIEs list: {}", e)))?;
let js_array = self
.converter
.expand_list(curies_vec.iter().map(String::as_str).collect())
.into_iter()
.map(JsValue::from)
.collect::<Array>();
Ok(JsValue::from(js_array))
}

#[wasm_bindgen(js_name = compressList)]
pub fn compress_list(&self, curies: JsValue) -> Result<JsValue, JsValue> {
let curies_vec: Vec<String> = serde_wasm_bindgen::from_value(curies)
.map_err(|e| JsValue::from_str(&format!("Error converting URIs list: {}", e)))?;
let js_array = self
.converter
.compress_list(curies_vec.iter().map(String::as_str).collect())
.into_iter()
.map(JsValue::from)
.collect::<Array>();
Ok(JsValue::from(js_array))
}

#[wasm_bindgen(js_name = toString)]
pub fn to_string(&self) -> String {
self.converter.to_string()
}

// #[wasm_bindgen(js_name = prefixMap)]
// pub fn prefix_map(&self) -> Result<JsValue, JsValue> {
// serde_wasm_bindgen::to_value(&self.converter.prefix_map).map_err(|e| e.into())
// }
}

/// Get OBO converter
Expand All @@ -108,9 +176,78 @@ pub fn get_obo_converter_js() -> Promise {
})
}

/// Get Bioregistry converter
#[wasm_bindgen(js_name = getBioregistryConverter)]
pub fn get_bioregistry_converter_js() -> Promise {
future_to_promise(async move {
match get_bioregistry_converter().await {
Ok(converter) => Ok(JsValue::from(ConverterJs { converter })),
Err(e) => Err(JsValue::from_str(&format!(
"Error getting Bioregistry converter: {e}"
))),
}
})
}

/// Get GO converter
#[wasm_bindgen(js_name = getGoConverter)]
pub fn get_go_converter_js() -> Promise {
future_to_promise(async move {
match get_go_converter().await {
Ok(converter) => Ok(JsValue::from(ConverterJs { converter })),
Err(e) => Err(JsValue::from_str(&format!(
"Error getting GO converter: {e}"
))),
}
})
}

/// Get Monarch converter
#[wasm_bindgen(js_name = getMonarchConverter)]
pub fn get_monarch_converter_js() -> Promise {
future_to_promise(async move {
match get_monarch_converter().await {
Ok(converter) => Ok(JsValue::from(ConverterJs { converter })),
Err(e) => Err(JsValue::from_str(&format!(
"Error getting Monarch converter: {e}"
))),
}
})
}

// impl Into<JsValue> for RecordJs {
// fn into(self) -> JsValue {
// // JsValue::from_serde(&self).unwrap()
// self.to_js()
// }
// }

// NOTE: we cannot easily convert a JS object to a string in Rust, it needs to be done in JS with JSON.stringify()
// fn get_str_from_obj(obj: JsValue) -> Result<String, JsValue> {
// if obj.is_string() {
// obj.as_string().ok_or_else(|| JsValue::from_str("String conversion failed"))
// } else if obj.is_object() {
// let str: String = serde_wasm_bindgen::from_value(obj)
// .map_err(|e| JsValue::from_str(&format!("Failed to serialize JSON: {}", e)))?;
// Ok(str)
// } else {
// return Err(JsValue::from_str("Expected a string or a JSON object"));
// }
// }

// #[wasm_bindgen(static_method_of = ConverterJs)]
// pub fn chain(converters: &JsValue) -> Promise {
// future_to_promise(async move {
// let converters_vec: Vec<ConverterJs> = serde_wasm_bindgen::from_value(converters).map_err(|e| {
// JsValue::from_str(&format!("Error converting converters list: {}", e))
// })?;
// let rust_converters: Vec<Converter> = converters_vec
// .into_iter()
// .map(|converter_js| converter_js.converter)
// .collect();
// match Converter::chain(rust_converters) {
// Ok(converter) => Ok(JsValue::from(ConverterJs { converter })),
// Err(e) => Err(JsValue::from_str(&e.to_string())),
// }
// })
// }
119 changes: 119 additions & 0 deletions js/tests/curies.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import {describe, expect, test} from '@jest/globals';
import {Record, Converter, getOboConverter, getBioregistryConverter} from "../pkg/node";

describe('Tests for the curies npm package', () => {
// NOTE: `await init()` only needed in browser environment

test('from empty converter', async () => {
const converter = new Converter();
const record1 = new Record("DOID", "http://purl.obolibrary.org/obo/DOID_", [], [])
converter.addRecord(record1);
converter.addCurie("OBO", "http://purl.obolibrary.org/obo/");
expect(converter.compress("http://purl.obolibrary.org/obo/DOID_1234")).toBe("DOID:1234");
expect(converter.expand("OBO:1234")).toBe("http://purl.obolibrary.org/obo/1234");
expect(converter.expandList(["OBO:1234", "DOID:1234", "Wrong:1"])).toEqual([
"http://purl.obolibrary.org/obo/1234",
"http://purl.obolibrary.org/obo/DOID_1234",
undefined
]);
expect(converter.compress("http://purl.obolibrary.org/obo/1234")).toBe("OBO:1234");
expect(converter.compressList([
"http://purl.obolibrary.org/obo/1234",
"http://purl.obolibrary.org/obo/DOID_1234",
"http://identifiers.org/DOID:1234"
])).toEqual(["OBO:1234", "DOID:1234", undefined]);
});

test('from prefix map', async () => {
const converter = await Converter.fromPrefixMap(`{
"GO": "http://purl.obolibrary.org/obo/GO_",
"DOID": "http://purl.obolibrary.org/obo/DOID_",
"OBO": "http://purl.obolibrary.org/obo/"
}`);
expect(converter.compress("http://purl.obolibrary.org/obo/DOID_1234")).toBe("DOID:1234");
expect(converter.expand("DOID:1234")).toBe("http://purl.obolibrary.org/obo/DOID_1234");
expect(converter.expandList(["OBO:1234", "DOID:1234", "Wrong:1"])).toEqual([
"http://purl.obolibrary.org/obo/1234",
"http://purl.obolibrary.org/obo/DOID_1234",
undefined
]);
expect(converter.compressList([
"http://purl.obolibrary.org/obo/1234",
"http://purl.obolibrary.org/obo/DOID_1234",
"http://identifiers.org/DOID:1234"
])).toEqual(["OBO:1234", "DOID:1234", undefined]);
});

test('from JSON-LD', async () => {
const converter = await Converter.fromJsonld(`{
"@context": {
"GO": "http://purl.obolibrary.org/obo/GO_",
"DOID": "http://purl.obolibrary.org/obo/DOID_",
"OBO": "http://purl.obolibrary.org/obo/"
}
}`);
expect(converter.compress("http://purl.obolibrary.org/obo/DOID_1234")).toBe("DOID:1234");
expect(converter.expand("DOID:1234")).toBe("http://purl.obolibrary.org/obo/DOID_1234");
});

test('from extended prefix map', async () => {
const converter = await Converter.fromExtendedPrefixMap(`[
{
"prefix": "DOID",
"prefix_synonyms": [
"doid"
],
"uri_prefix": "http://purl.obolibrary.org/obo/DOID_",
"uri_prefix_synonyms": [
"http://bioregistry.io/DOID:"
],
"pattern": "^\\\\d+$"
},
{
"prefix": "GO",
"prefix_synonyms": [
"go"
],
"uri_prefix": "http://purl.obolibrary.org/obo/GO_",
"pattern": "^\\\\d{7}$"
},
{
"prefix": "OBO",
"prefix_synonyms": [
"obo"
],
"uri_prefix": "http://purl.obolibrary.org/obo/"
}
]`);
expect(converter.compress("http://bioregistry.io/DOID:1234")).toBe("DOID:1234");
expect(converter.expand("doid:1234")).toBe("http://purl.obolibrary.org/obo/DOID_1234");
});

test('get OBO converter', async () => {
const converter = await getOboConverter();
expect(converter.compress("http://purl.obolibrary.org/obo/DOID_1234")).toBe("DOID:1234");
expect(converter.expand("DOID:1234")).toBe("http://purl.obolibrary.org/obo/DOID_1234");
});

test('get Bioregistry converter', async () => {
const converter = await getBioregistryConverter();
expect(converter.compress("http://purl.obolibrary.org/obo/DOID_1234")).toBe("doid:1234");
expect(converter.expand("doid:1234")).toBe("http://purl.obolibrary.org/obo/DOID_1234");
});

test('chain converters', async () => {
const customConverter1 = await Converter.fromPrefixMap(`{
"DOID": "http://purl.obolibrary.org/obo/SPECIAL_DOID_"
}`);
const customConverter2 = await Converter.fromPrefixMap(`{
"GO": "http://purl.obolibrary.org/obo/SPECIAL_GO_",
"DOID": "http://purl.obolibrary.org/obo/DOID_"
}`);
const bioregistryConverter = await getBioregistryConverter();
const converter = bioregistryConverter
.chain(customConverter1)
.chain(customConverter2)
expect(converter.compress("http://purl.obolibrary.org/obo/SPECIAL_DOID_1234")).toBe("DOID:1234");
});

});
2 changes: 1 addition & 1 deletion lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ categories.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
trie-rs = "0.1"
ptrie = "0.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.11", features = ["blocking", "json"] }
Expand Down
2 changes: 1 addition & 1 deletion lib/docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

- [Introduction](introduction.md)
- [Use Rust crate](use_rust.md)
- [Use Python package](use_python.md)
- [Use NPM package](use_javascript.md)
- [Use Python package](use_python.md)
- [Contributing](contributing.md)
Loading

0 comments on commit bf186e0

Please sign in to comment.