Skip to content

Commit dc9ada3

Browse files
committed
refact: fix bugs and make export work
1 parent 5292e10 commit dc9ada3

File tree

11 files changed

+252
-289
lines changed

11 files changed

+252
-289
lines changed

.github/dependabot.yml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
2+
3+
version: 2
4+
updates:
5+
- package-ecosystem: cargo
6+
directory: "/"
7+
schedule:
8+
interval: weekly
9+
day: friday

.github/exporter.sh

-16
This file was deleted.

.github/workflows/build.yml

-37
This file was deleted.

.gitignore

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
pkg/*.gem
2-
coverage/
3-
.coveralls.yml
4-
.bundle
5-
Gemfile.lock
61
target/
72
Cargo.lock
3+
.DS_Store
4+
*.svg

Cargo.toml

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
[package]
22
name = "githubchart"
3-
version = "0.1.0"
4-
authors = ["Les Aker <[email protected]>"]
5-
edition = "2018"
3+
version = "5.0.0"
4+
authors = ["frytg"]
5+
edition = "2021"
66

77
[dependencies]
8-
serde = { version = "1.0", features = ["derive"] }
9-
serde_json = "1.0"
10-
svg = "0.8"
11-
chrono = "0.4"
8+
reqwest = { version = "0.11", features = ["blocking", "json"] }
9+
scraper = "0.17"
10+
11+
[profile.release]
12+
# optimizations from https://github.com/johnthagen/min-sized-rust
13+
opt-level = 'z' # optimize for size
14+
lto = true # enable link time optimization
15+
codegen-units = 1 # reduce parallel codegen units to increase optimizations
16+
panic = 'abort' # abort on panic
17+
strip = true # strip symbols from binary

README.md

+35-19
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,51 @@
1-
GithubChart
2-
============
1+
# GitHubChart (The Rusty Version)
32

4-
[![Crate Version](https://img.shields.io/crates/v/githubchart.svg)](https://crates.io/crates/githubchart)
5-
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/akerl/githubchart/build.yml?branch=main)](https://github.com/akerl/githubchart/actions)
6-
[![MIT Licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://tldrlegal.com/license/mit-license)
3+
Generates an SVG of your GitHub contributions:
74

8-
Generates an SVG of your Github contributions:
5+
![Example image](./assets/frytg.svg)
96

10-
![Example image](http://akerl.github.io/githubchart/chart.svg)
7+
## Fork
118

12-
![Other user example](http://akerl.github.io/githubchart/other_user.svg)
9+
This is forked from [githubchart](https://github.com/akerl/githubchart) and ported from Ruby to Rust. It does not provide 100% of the same functionality, but it does generate a similar SVG.
1310

14-
## Usage
11+
## Usage with `cargo`
1512

16-
Run `githubchart path/to/svg` to generate an SVG. To override the default username (pulled from your local shell or .gitconfig), use `githubchart -u username path/to/svg`
13+
If you have Rust installed and are familiar with cargo, you can install and run this directly:
1714

18-
GithubChart also allows you to provide input from a file instead of pulling data from Github. You can pass JSON to GithubChart by using `githubchart -i /path/to/file /path/to/svg`, or use '-' to use STDIN. See spec/examples/input.json for example data.
15+
```sh
16+
cargo run -- output.svg -u frytg
17+
```
1918

20-
If you don't provide a file path, the resulting SVG will be printed to stdout.
19+
This compiles and runs the program directly (using dev profile and debug symbols). This would also be the command when developing locally.
2120

22-
To modify the color scheme used, you can provide `-c SCHEME`. For example, `githubchart -c halloween` uses GitHub's halloween colors. Use `-s` to list the available schemes.
21+
To modify the color scheme used, you can provide `-c SCHEME`. For example, `cargo run -- output.svg -u frytg -c halloween` uses GitHub's halloween colors.
2322

24-
### Hosted SVG
23+
Use `cargo fmt` to format the code.
2524

26-
A hosted service for loading these SVGs was made by [2016rshah](https://github.com/2016rshah): http://ghchart.rshah.org/ ([source code](https://github.com/2016rshah/githubchart-api))
25+
## Usage with binary
2726

28-
## Installation
27+
Alternatively, you can download a release binary from the [releases page](https://github.com/frytg/githubchart-rust/releases) and run it directly:
2928

30-
cargo install githubchart
29+
```sh
30+
./githubchart output.svg -u frytg
31+
```
3132

32-
## License
33+
## Build
34+
35+
You can build a release binary with:
36+
37+
```sh
38+
cargo build --release
39+
```
40+
41+
[`Cargo.toml`](./Cargo.toml) is configured to optimize for size.
3342

34-
githubchart is released under the MIT License. See the bundled LICENSE file for details.
43+
Test the binary with:
44+
45+
```sh
46+
./target/release/githubchart release.svg -u frytg
47+
```
48+
49+
## License
3550

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

bin/githubchart

-67
This file was deleted.

src/lib.rs

+70-9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use reqwest;
2+
use scraper::{Html, Selector};
3+
14
pub mod svg;
25

36
pub struct Chart {
@@ -14,17 +17,75 @@ impl Chart {
1417
}
1518
}
1619

17-
pub fn render(&self, format: &str) -> Result<String, String> {
18-
match format {
19-
"svg" => Ok(svg::render_svg(&self)),
20-
"svg_square" => Ok(svg::render_svg_square(&self)),
21-
_ => Err(format!("Format {} is unsupported.", format)),
22-
}
20+
pub fn render(&self) -> Result<String, String> {
21+
Ok(svg::render_svg(&self))
2322
}
2423
}
2524

2625
pub const COLOR_SCHEMES: &[(&str, &[&str])] = &[
27-
("default", &["#eeeeee", "#c6e48b", "#7bc96f", "#239a3b", "#196127"]),
28-
("old", &["#eeeeee", "#d6e685", "#8cc665", "#44a340", "#1e6823"]),
29-
("halloween", &["#EEEEEE", "#FFEE4A", "#FFC501", "#FE9600", "#03001C"]),
26+
(
27+
"default",
28+
&["#eeeeee", "#c6e48b", "#7bc96f", "#239a3b", "#196127"],
29+
),
30+
(
31+
"old",
32+
&["#eeeeee", "#d6e685", "#8cc665", "#44a340", "#1e6823"],
33+
),
34+
(
35+
"halloween",
36+
&["#EEEEEE", "#FFEE4A", "#FFC501", "#FE9600", "#03001C"],
37+
),
3038
];
39+
40+
pub fn fetch_github_stats(
41+
username: &str,
42+
) -> Result<Vec<(String, i32)>, Box<dyn std::error::Error>> {
43+
// GitHub's contribution graph data endpoint
44+
let url = format!("https://github.com/users/{}/contributions", username);
45+
46+
// Fetch the HTML content
47+
let client = reqwest::blocking::Client::new();
48+
let response = client
49+
.get(&url)
50+
.header("User-Agent", "githubchart-rust")
51+
.send()?;
52+
53+
// Check status code
54+
if !response.status().is_success() {
55+
return Err(format!(
56+
"Failed loading data from GitHub: {} {}",
57+
url,
58+
response.status()
59+
)
60+
.into());
61+
}
62+
63+
let html_content = response.text()?;
64+
let document = Html::parse_document(&html_content);
65+
66+
// Select contribution calendar cells
67+
let cell_selector = Selector::parse("td.ContributionCalendar-day").unwrap();
68+
69+
let mut stats = Vec::new();
70+
71+
for element in document.select(&cell_selector) {
72+
if let (Some(date), Some(count_str)) = (
73+
element.value().attr("data-date"),
74+
element.value().attr("data-level"),
75+
) {
76+
if let Ok(count) = count_str.parse::<i32>() {
77+
// println!("fetch_github_stats > date: {}, count: {}", date, count);
78+
stats.push((date.to_string(), count));
79+
}
80+
}
81+
}
82+
83+
// Sort by date
84+
stats.sort_by(|a, b| a.0.cmp(&b.0));
85+
86+
if stats.is_empty() {
87+
println!("Warning: No contribution data found for user {}", username);
88+
}
89+
90+
Ok(stats)
91+
}

0 commit comments

Comments
 (0)