Semver on the web with Rust and WebAssembly

This page lets you compare a semver requirement to a particular version to see if the version fulfills that requirement. Basically, it's like semver.npmjs.com but less fully-featured.

Additionally, this page is built by compiling Rust's semver crate to WebAssembly, and then running that in the browser.

Does it match?

Some examples to try:

VersionRequirementOutput
1.0.01.0.0match
0.1.01.0.0not a match
1.2.3^ 1.0.0match
0.3.0> 0.1.0, < 0.5.0match

Disclaimers

Note that a bare version number defaults to a ^ requirement. For more, see the docs.

Any error returns "not a match", including failing of parsing.

I'm very bad at front-end dev, so the code is likely very bad.

Apparently if I used `wasm-opt` and `opt-level s` instead of `opt-level 3` this gets smaller, to around 60k.

Also, this code is a bit of a hack; we have some ABI issues to work out between JS/Rust interop. For more details on this, see killercup/wasm-experiments, which is also where I got some of the code from.

Rust source code for the wasm file:

extern crate semver;

use semver::Version;
use semver::VersionReq;

use std::mem;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};

macro_rules! check {
    ($expr:expr) => (match $expr {
        Ok(val) => val,
        Err(_) => return false,
    })
}

#[no_mangle]
pub extern fn is_match(v: *mut c_char, r: *mut c_char) -> bool {
    unsafe {
        let v = CStr::from_ptr(v);
        let v = check!(v.to_str());

        let r = CStr::from_ptr(r);
        let r = check!(r.to_str());
        
        let v = check!(Version::parse(v));

        let r = check!(VersionReq::parse(r));

        r.matches(&v)
    }
}

// this shenanigans is due to it being tough to deal with JS strings into wasm,
// see the JS code for more.

#[no_mangle]
pub extern "C" fn alloc(size: usize) -> *mut c_void {
    let mut buf = Vec::with_capacity(size);
    let ptr = buf.as_mut_ptr();
    mem::forget(buf); // This is JS' responsibility now
    return ptr as *mut c_void;
}

#[no_mangle]
pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
    unsafe  {
        let _buf = Vec::from_raw_parts(ptr, 0, cap);
    }
}

#[cfg(test)]
mod tests {
    use std::ffi::CString;
    use super::is_match;

    #[test]
    fn matches() {
        // these are going to leak but it's two tests so I don't care
        let v = CString::new("1.0.0").unwrap().into_raw();
        let r = CString::new(">= 1.0.0").unwrap().into_raw();

        assert!(is_match(v, r));
    }

    #[test]
    fn does_not_match() {
        // these are going to leak but it's two tests so I don't care
        let v = CString::new("0.1.0").unwrap().into_raw();
        let r = CString::new(">= 1.0.0").unwrap().into_raw();

        assert!(!is_match(v, r));
    }
}