Skip to content

Commit

Permalink
Attempt to package rust for the browser
Browse files Browse the repository at this point in the history
Note that this was done in an environment where I cannot test
So a failure is not that unexpected
  • Loading branch information
LivInTheLookingGlass committed Aug 14, 2024
1 parent 53e36c3 commit aef1d70
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 56 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ jobs:
with:
node-version: 22

- uses: actions-rs/toolchain@v1
with:
toolchain: stable

- run: rustup default stable

- name: Install git, linguist
uses: awalsh128/cache-apt-pkgs-action@latest
with:
Expand Down
4 changes: 4 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ _static/dist/bundle.js:
@mkdir -p _static/dist
@cp ../javascript/dist/bundle-js.js _static/dist/bundle-js.js

_static/dist/rust.js:
@cd ../rust && cargo build --target wasm32-unknown-unknown --features "wasm" && \
wasm-bindgen target/wasm32-unknown-unknown/release/rust.wasm --out-dir ../docs/dist/

_static/dist/python.tar.gz:
@$(MAKE) -C ../python webpack $(MFLAGS)

Expand Down
73 changes: 73 additions & 0 deletions docs/_static/test-rs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!-- test/support/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript Tests (Packaged with WebPack + Babel)</title>

<link href="https://npmcdn.com/[email protected]/mocha.css" rel="stylesheet" />
<script src="https://npmcdn.com/[email protected]/mocha.js"></script>
</head>
<body>

<!-- A container element for the visual Mocha results -->
<div id="mocha"></div>

<!-- Mocha setup and initiation code -->
<script>
mocha.setup('bdd');
window.onload = function() {
var runner = mocha.run();
var failedTests = [];

runner.on('end', function() {
window.mochaResults = runner.stats;
window.mochaResults.reports = failedTests;
});

runner.on('fail', logFailure);

function logFailure(test, err){
var flattenTitles = function(test){
var titles = [];
while (test.parent.title){
titles.push(test.parent.title);
test = test.parent;
}
return titles.reverse();
};

failedTests.push({
name: test.title,
result: false,
message: err.message,
stack: err.stack,
titles: flattenTitles(test)
});
};
};
</script>
<script type="module">
import init, { get_problems, run_problem, get_answer } from './dist/rust.js';
async function runTests() {
await init();
for (const p of get_problems()) {
const expected = await get_answer(p);
describe(`run test ${p}`, function() {
this.timeout(Infinity);
it(`should return ${expected}`, async () => {
const answer = await run_problem(p);
console.log(p, answer, expected);
if (answer !== expected) {
throw new Error();
}
});
});
}
}

runTests();
</script>

</body>
</html>
9 changes: 9 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ itertools = "0.13.0"
num-traits = "0.2.19"
rstest = "0.21.0"
seq-macro = "0.3.5"
wasm-bindgen = "0.2"
serde_derive = "1.0"

[lib]
crate-type = ["cdylib"] # Necessary for WebAssembly builds

[features]
default = []
wasm = ["wasm-bindgen"]

[profile.dev.package."*"]
# Set the default for dependencies in Development mode.
Expand Down
68 changes: 68 additions & 0 deletions rust/src/include/problems.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use seq_macro::seq;

pub mod include;
pub use include::utils::{Answer,get_answer};
seq!(N in 0001..=0020 {
mod p~N;
});
mod p0022;
mod p0024;
mod p0027;
mod p0034;
mod p0069;
mod p0076;
mod p0077;
mod p0087;
mod p0357;
mod p0836;

type ProblemType = fn() -> Answer;
type ProblemRef<'a> = (&'a usize, ProblemType, bool);

fn get_problem<'b>(n: usize) -> Option<ProblemRef<'b>> {
return match n {
1 => Some(( &1, p0001::p0001, false)),
2 => Some(( &2, p0002::p0002, false)),
3 => Some(( &3, p0003::p0003, false)),
4 => Some(( &4, p0004::p0004, false)),
5 => Some(( &5, p0005::p0005, false)),
6 => Some(( &6, p0006::p0006, false)),
7 => Some(( &7, p0007::p0007, false)),
8 => Some(( &8, p0008::p0008, false)),
9 => Some(( &9, p0009::p0009, false)),
10 => Some(( &10, p0010::p0010, false)),
11 => Some(( &11, p0011::p0011, false)),
12 => Some(( &12, p0012::p0012, false)),
13 => Some(( &13, p0013::p0013, false)),
14 => Some(( &14, p0014::p0014, false)),
15 => Some(( &15, p0015::p0015, false)),
16 => Some(( &16, p0016::p0016, false)),
17 => Some(( &17, p0017::p0017, false)),
18 => Some(( &18, p0018::p0018, false)),
19 => Some(( &19, p0019::p0019, false)),
20 => Some(( &20, p0020::p0020, false)),
22 => Some(( &22, p0022::p0022, false)),
24 => Some(( &24, p0024::p0024, false)),
27 => Some(( &27, p0027::p0027, false)),
34 => Some(( &34, p0034::p0034, false)),
69 => Some(( &69, p0069::p0069, false)),
76 => Some(( &76, p0076::p0076, false)),
77 => Some(( &77, p0077::p0077, false)),
87 => Some(( &87, p0087::p0087, false)),
357 => Some((&357, p0357::p0357, true)),
836 => Some((&836, p0836::p0836, false)),
_ => None,
};
}


pub fn generate_supported_problems() -> Vec<usize> {
let mut supported_problems = Vec::new();
for n in 1..10000 {
if get_problem(n).is_some() {
supported_problems.push(n);
}
}

return supported_problems;
}
23 changes: 23 additions & 0 deletions rust/src/include/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#[cfg(not(feature = "wasm"))]
use std::fs::read_to_string;
#[cfg(not(feature = "wasm"))]
use std::path::Path;

#[derive(Debug, PartialEq, Eq)]
Expand All @@ -7,6 +9,27 @@ pub enum Answer {
Int(i128),
}

#[cfg(feature = "wasm")]
const ANSWERS_TSV: &str = include_str!("../../../_data/answers.tsv");
#[cfg(feature = "wasm")]
const P0022_NAMES_TXT: &str = include_str!("../../../_data/p0022_names.txt");
#[cfg(feature = "wasm")]
const P0042_WORDS_TXT: &str = include_str!("../../../_data/p0042_words.txt");
#[cfg(feature = "wasm")]
const P0067_TRIANGLE_TXT: &str = include_str!("../../../_data/p0067_triangle.txt");

#[cfg(feature = "wasm")]
pub fn get_data_file(name: &str) -> String {
return match name {
"answers.tsv" => ANSWERS_TSV.to_string(),
"p0022_names.txt" => P0022_NAMES_TXT.to_string(),
"p0042_words.txt" => P0042_WORDS_TXT.to_string(),
"p0067_triangle.txt" => P0067_TRIANGLE_TXT.to_string(),
_ => panic!("Unknown file name: {}", name),
}
}

#[cfg(not(feature = "wasm"))]
pub fn get_data_file(name: &str) -> String {
let data_file = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().join("_data").join(name);
return read_to_string(&data_file).unwrap();
Expand Down
29 changes: 29 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

#[cfg(feature = "wasm")]
pub use include::problems::{generate_supported_problems,get_problem};
#[cfg(feature = "wasm")]
pub use include::utils::get_answer;

#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn run_problem(n: usize) -> JsValue {
let (problem_number, problem_function, _) = get_problem(n);
let answer = problem_function();
JsValue::from_serde(&answer).unwrap_or_else(|err| JsValue::from_str(&format!("Serialization error: {}", err)));
}

#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn get_problems() -> JsValue {
let answer = generate_supported_problems();
JsValue::from_serde(&answer).unwrap_or_else(|err| JsValue::from_str(&format!("Serialization error: {}", err)));
}

#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn get_answer(n: usize) -> JsValue {
let answer = get_answer(n);
JsValue::from_serde(&answer).unwrap_or_else(|err| JsValue::from_str(&format!("Serialization error: {}", err)));
}
70 changes: 14 additions & 56 deletions rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use itertools::Itertools;
pub mod include;
use include::math;
use include::primes;
use include::problems::{generate_supported_problems,get_problem};
use include::utils::{Answer,get_answer};
seq!(N in 0001..=0020 {
mod p~N;
Expand All @@ -30,70 +31,27 @@ mod p0087;
mod p0357;
mod p0836;

type ProblemType = fn() -> Answer;
type ProblemRef<'a> = (&'a usize, ProblemType, bool);

fn get_problem<'b>(n: usize) -> ProblemRef<'b> {
return match n {
1 => ( &1, p0001::p0001, false),
2 => ( &2, p0002::p0002, false),
3 => ( &3, p0003::p0003, false),
4 => ( &4, p0004::p0004, false),
5 => ( &5, p0005::p0005, false),
6 => ( &6, p0006::p0006, false),
7 => ( &7, p0007::p0007, false),
8 => ( &8, p0008::p0008, false),
9 => ( &9, p0009::p0009, false),
10 => ( &10, p0010::p0010, false),
11 => ( &11, p0011::p0011, false),
12 => ( &12, p0012::p0012, false),
13 => ( &13, p0013::p0013, false),
14 => ( &14, p0014::p0014, false),
15 => ( &15, p0015::p0015, false),
16 => ( &16, p0016::p0016, false),
17 => ( &17, p0017::p0017, false),
18 => ( &18, p0018::p0018, false),
19 => ( &19, p0019::p0019, false),
20 => ( &20, p0020::p0020, false),
22 => ( &22, p0022::p0022, false),
24 => ( &24, p0024::p0024, false),
27 => ( &27, p0027::p0027, false),
34 => ( &34, p0034::p0034, false),
69 => ( &69, p0069::p0069, false),
76 => ( &76, p0076::p0076, false),
77 => ( &77, p0077::p0077, false),
87 => ( &87, p0087::p0087, false),
357 => (&357, p0357::p0357, true),
836 => (&836, p0836::p0836, false),
_ => panic!(),
};
}

#[cfg(not(test))]
fn main() {
let sieve = primes::primes::<u64>().take(10);
for i in sieve {
println!("{}", i);
}
let mut answers: Vec<usize> = (1..=20).collect();
answers.push(22);
answers.push(24);
answers.push(27);
answers.push(34);
answers.push(69);
answers.push(76);
answers.push(77);
answers.push(87);
answers.push(357);
let supported = generate_supported_problems();

for id in answers {
let (_, func, _) = get_problem(id);
let answer = get_answer(id);
let result = func();
match (answer, result) {
(Answer::String(e), Answer::String(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r),
(Answer::Int(e), Answer::Int(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r),
_ => panic!("Type mismatch in answer return"),
for id in supported {
match get_problem(id) {
Some((_, func, _)) => {
let answer = get_answer(id);
let result = func();
match (answer, result) {
(Answer::String(e), Answer::String(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r),
(Answer::Int(e), Answer::Int(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r),
_ => panic!("Type mismatch in answer return"),
}
}
None => panic!("Problem not found"),
}
}
}
Expand Down

0 comments on commit aef1d70

Please sign in to comment.