Skip to content

Commit 6306ccc

Browse files
committed
feat: add WASM build
1 parent 088eb1a commit 6306ccc

File tree

9 files changed

+149
-89
lines changed

9 files changed

+149
-89
lines changed

CHANGELOG.md

+7-51
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,13 @@
1-
# 4.0.0 / 2023-07-18
1+
# Changelog
22

3-
* [BUGFIX] Update GithubStats dep to use new Github.com format for table
3+
## 5.1.0 / 2024-11-13
44

5-
# 3.4.0 / 2023-01-08
5+
- feat: add WASM build
66

7-
* [ENHANCEMENT] Update GithubStats dep
7+
## 5.0.0 / 2024-11-12
88

9-
# 3.3.1 / 2021-02-06
9+
- feat: migrate to Rust
1010

11-
* [BUGFIX] Actually bump githubstats dep
12-
13-
# 3.3.0 / 2021-02-06
14-
15-
* [ENHANCEMENT] Update GithubStats dep
16-
17-
# 3.2.0 / 2020-10-06
18-
19-
* [ENHANCEMENT] Update GithubStats dep
20-
21-
# 3.1.1 / 2020-08-13
22-
23-
* [ENHANCEMENT] Update GithubStats dep
24-
25-
# 3.1.0 / 2017-10-03
26-
27-
* [ENHANCEMENT] GitHub updated their styling; this updated matches that change
28-
29-
# 3.0.0 / 2017-09-29
30-
31-
* [ENHANCEMENT] Upgrade to latest githubstats with better error messages
32-
33-
# 2.1.0 / 2017-08-25
34-
35-
* [ENHANCEMENT] Upgrade to latest githubstats
36-
37-
# 2.0.1 / 2016-11-08
38-
39-
* [BUGFIX] Actually update changelog in release
40-
41-
# 2.0.0 / 2016-11-07
42-
43-
* [FEATURE] Add support for svg_square as a rendering type, thanks @blerchin
44-
45-
# 1.1.0 / 2016-05-19
46-
47-
* [BUGFIX] Upgrade to newer GithubStats that fixes streak parsing
48-
49-
# 1.0.1 / 2015-10-20
50-
51-
* [BUGFIX] Fix month labels for parity with GitHub
52-
* [ENHANCEMENT] Link to @2016rshah's awesome service for hosted SVGs
53-
54-
# 1.0.0 / 2015-01-25
55-
56-
* [ENHANCEMENT] Stabilized API
11+
## 4.0.0 / 2023-07-18
5712

13+
See [upstream repo](https://github.com/akerl/githubchart) for previouschangelog.

Cargo.lock

+34-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
[package]
2-
name = "githubchart"
3-
version = "5.0.0"
2+
name = "githubchart-rust"
3+
version = "5.1.0"
44
authors = ["frytg"]
55
edition = "2021"
6+
license = "MIT"
7+
repository = "https://github.com/frytg/githubchart-rust"
8+
description = "GitHub contribution graph generator in Rust/WASM"
9+
10+
[lib]
11+
crate-type = ["cdylib", "rlib"]
612

713
[dependencies]
814
regex = "1.11.1"
9-
reqwest = { version = "0.11", features = ["blocking", "json"] }
15+
reqwest = { version = "0.11", features = ["json"] }
1016
scraper = "0.17"
17+
wasm-bindgen = "0.2"
18+
wasm-bindgen-futures = "0.4"
19+
serde = { version = "1.0", features = ["derive"] }
20+
serde-wasm-bindgen = "0.5"
21+
getrandom = { version = "0.2", features = ["js"] }
22+
23+
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
24+
tokio = { version = "1.0", features = ["rt-multi-thread", "macros"] }
25+
26+
[target.'cfg(target_arch = "wasm32")'.dependencies]
27+
js-sys = "0.3"
28+
tokio = { version = "1.0", features = ["sync", "macros"] }
29+
getrandom = { version = "0.2", features = ["js"] }
1130

1231
[profile.release]
1332
# optimizations from https://github.com/johnthagen/min-sized-rust

README.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,23 @@ Test the binary with:
4646
./target/release/githubchart release.svg -u frytg
4747
```
4848

49+
## Build for Web
50+
51+
See [_Compiling from Rust to WebAssembly_](https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_Wasm) for a full guide on compiling Rust to WebAssembly (WASM).
52+
53+
This project is already configured to build for Web with `wasm-pack`. Run this command to build:
54+
55+
```sh
56+
wasm-pack build --target web
57+
```
58+
59+
There's also an example in [`web/example.html`](./web/example.html) that you can run locally.
60+
61+
More docs about this:
62+
63+
- [WebAssembly in Deno](https://docs.deno.com/runtime/reference/wasm/)
64+
- [`wasm-pack` docs](https://rustwasm.github.io/docs/wasm-pack/)
65+
4966
## License
5067

51-
This `githubchart-rust` fork (like the upstream repo) is released under the MIT License. See the bundled [LICENSE](./LICENSE) file for details.
68+
This `githubchart-rust` fork (like the upstream repository) is released under the MIT License. See the bundled [LICENSE](./LICENSE) file for details.

src/lib.rs

+24-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use reqwest;
22
use scraper::{Html, Selector};
3+
use wasm_bindgen::prelude::*;
34

45
pub mod svg;
56

@@ -37,20 +38,18 @@ pub const COLOR_SCHEMES: &[(&str, &[&str])] = &[
3738
),
3839
];
3940

40-
pub fn fetch_github_stats(
41+
pub async fn fetch_github_stats(
4142
username: &str,
4243
) -> Result<Vec<(String, i32)>, Box<dyn std::error::Error>> {
43-
// GitHub's contribution graph data endpoint
4444
let url = format!("https://github.com/users/{}/contributions", username);
4545

46-
// Fetch the HTML content
47-
let client = reqwest::blocking::Client::new();
46+
let client = reqwest::Client::new();
4847
let response = client
4948
.get(&url)
5049
.header("User-Agent", "githubchart-rust")
51-
.send()?;
50+
.send()
51+
.await?;
5252

53-
// Check status code
5453
if !response.status().is_success() {
5554
return Err(format!(
5655
"Failed loading data from GitHub: {} {}",
@@ -60,10 +59,8 @@ pub fn fetch_github_stats(
6059
.into());
6160
}
6261

63-
let html_content = response.text()?;
62+
let html_content = response.text().await?;
6463
let document = Html::parse_document(&html_content);
65-
66-
// Select contribution calendar cells
6764
let cell_selector = Selector::parse("td.ContributionCalendar-day").unwrap();
6865

6966
let mut stats = Vec::new();
@@ -74,13 +71,11 @@ pub fn fetch_github_stats(
7471
element.value().attr("data-level"),
7572
) {
7673
if let Ok(count) = count_str.parse::<i32>() {
77-
// println!("fetch_github_stats > date: {}, count: {}", date, count);
7874
stats.push((date.to_string(), count));
7975
}
8076
}
8177
}
8278

83-
// Sort by date
8479
stats.sort_by(|a, b| a.0.cmp(&b.0));
8580

8681
if stats.is_empty() {
@@ -92,3 +87,21 @@ pub fn fetch_github_stats(
9287

9388
#[cfg(test)]
9489
mod tests;
90+
91+
#[wasm_bindgen]
92+
pub async fn generate_github_chart(username: &str, color_scheme: Option<String>) -> Result<String, JsValue> {
93+
let stats = fetch_github_stats(username)
94+
.await
95+
.map_err(|e| JsValue::from_str(&e.to_string()))?;
96+
97+
let colors = match color_scheme.as_deref() {
98+
Some(scheme) => COLOR_SCHEMES
99+
.iter()
100+
.find(|&&(name, _)| name == scheme)
101+
.map(|&(_, colors)| colors.to_vec()),
102+
None => None,
103+
};
104+
105+
let chart = Chart::new(stats, colors);
106+
chart.render().map_err(|e| JsValue::from_str(&e))
107+
}

src/main.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ use std::fs::File;
33
use std::io::{self, Write};
44
use std::path::Path;
55

6-
use githubchart::{fetch_github_stats, Chart, COLOR_SCHEMES};
6+
use githubchart_rust::{fetch_github_stats, Chart, COLOR_SCHEMES};
77

8-
fn main() {
8+
#[tokio::main]
9+
async fn main() {
910
let args: Vec<String> = env::args().collect();
1011
if args.len() < 2 {
1112
eprintln!(
@@ -49,8 +50,7 @@ fn main() {
4950
}
5051

5152
let stats = if let Some(username) = username {
52-
// Fetch stats from GitHub API
53-
match fetch_github_stats(&username) {
53+
match fetch_github_stats(&username).await {
5454
Ok(stats) => stats,
5555
Err(e) => {
5656
eprintln!("Error fetching GitHub stats: {}", e);

tests/integration_tests.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
use regex::Regex;
2+
use githubchart_rust::{fetch_github_stats, Chart, COLOR_SCHEMES};
23

3-
use githubchart::{fetch_github_stats, Chart, COLOR_SCHEMES};
4-
5-
#[test]
6-
fn test_github_stats_fetching() {
7-
// Note: This test requires internet connection
8-
match fetch_github_stats("frytg") {
4+
#[tokio::test]
5+
async fn test_github_stats_fetching() {
6+
match fetch_github_stats("frytg").await {
97
Ok(stats) => {
108
assert!(!stats.is_empty());
11-
// Check date format
129
assert!(Regex::new(r"^\d{4}-\d{2}-\d{2}$")
1310
.unwrap()
1411
.is_match(&stats[0].0));
15-
// Check contribution count is non-negative
1612
assert!(stats[0].1 >= 0);
1713
}
1814
Err(e) => panic!("Failed to fetch stats: {}", e),

web/example.html

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>GitHub Chart Demo</title>
6+
</head>
7+
<body>
8+
<div id="chart"></div>
9+
<script type="module">
10+
import { initWasm, generateGitHubChart } from "./index.js";
11+
12+
async function init() {
13+
await initWasm();
14+
const svg = await generateGitHubChart("frytg", "default");
15+
document.getElementById("chart").innerHTML = svg;
16+
}
17+
18+
init();
19+
</script>
20+
</body>
21+
</html>

web/index.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import init, { generate_github_chart } from '../pkg/githubchart.js'
2+
3+
export async function initWasm() {
4+
await init()
5+
}
6+
7+
export async function generateGitHubChart(username, colorScheme) {
8+
return generate_github_chart(username, colorScheme)
9+
}
10+
11+
export const COLOR_SCHEMES = {
12+
default: ['#eeeeee', '#c6e48b', '#7bc96f', '#239a3b', '#196127'],
13+
old: ['#eeeeee', '#d6e685', '#8cc665', '#44a340', '#1e6823'],
14+
halloween: ['#EEEEEE', '#FFEE4A', '#FFC501', '#FE9600', '#03001C'],
15+
}

0 commit comments

Comments
 (0)