-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.rs
132 lines (113 loc) · 4.02 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#![forbid(unsafe_code)]
extern crate chrono;
extern crate clap;
extern crate ureq;
use clap::{crate_version, Command, Arg, ArgAction};
use serde::Deserialize;
use chrono::{DateTime, Local};
use crate::chrono::Datelike;
#[derive(Deserialize, Debug)]
struct UserEventsApiResponse {
created_at: String,
r#type: String,
payload: UserEventsPayload,
}
#[derive(Deserialize, Debug)]
struct UserEventsPayload {
ref_type: Option<String>,
}
fn describe_event(event: &UserEventsApiResponse) -> String {
match (event.r#type.as_str(), &event.payload.ref_type) {
("CreateEvent", Some(ref_type)) => format!("CreateEvent ({})", ref_type),
// TODO: Consider other event types: https://developer.github.com/v3/activity/events/types/
_ => format!("{}", event.r#type),
}
}
fn look_for_events(data: Vec<UserEventsApiResponse>, verbose: u8) {
let today = Local::now();
let todays_events = data.iter().filter(|x| {
let dt = DateTime::parse_from_rfc3339(&x.created_at).unwrap(); // TODO: Is this the right date/time standard?
let dt_local = dt.with_timezone(&Local);
let is_today = dt_local.year() == today.year()
&& dt_local.month() == today.month()
&& dt_local.day() == today.day();
if is_today && verbose > 0 {
println!("{} at {}", describe_event(x), dt_local.format("%H:%M:%S").to_string());
}
is_today
});
// In addition to it's obvious purpose, I'm using the count method call
// below to cause side-effects in the filter above (print out today's
// events when verbose > 0) ... that doesn't seem so nice but it does
// save me from having to parse the datetime and convert it to local
// time again. Perhaps a filter_map would be better ... that could
// filter the events, returning today's events in local time along with
// the event type, ready to be printed when verbose > 0. Then hopefully
// I can find some way to short circuit evaluation of the filter_map
// in cases where verbose < 1.
if todays_events.count() > 0 {
println!("Yes");
} else {
println!("No");
}
}
#[derive(Debug)]
enum MyError {
Io(std::io::Error),
Ureq(ureq::Error),
}
impl From<std::io::Error> for MyError {
fn from(err: std::io::Error) -> MyError {
MyError::Io(err)
}
}
impl From<ureq::Error> for MyError {
fn from(err: ureq::Error) -> MyError {
MyError::Ureq(err)
}
}
fn get_and_parse_json(url: &str, verbose: u8) -> Result<Vec<UserEventsApiResponse>, MyError> {
if verbose > 1 {
println!("Fetching {}", url);
}
// TODO: Set user-agent header - https://developer.github.com/v3/#user-agent-required
let resp: Vec<UserEventsApiResponse> = ureq::get(url)
.set("Accept", "application/vnd.github.v3+json")
.call()?
.into_json()?;
if verbose > 2 {
// Super verbose!
println!("{:?}", resp);
}
Ok(resp)
}
macro_rules! die {
($($tt:tt)*) => {{
eprintln!($($tt)*);
::std::process::exit(1)
}}
}
fn main() {
let matches = Command::new("Did I Github today?")
.version(crate_version!())
.about("An command line application to tell you if you've done anything on Github today")
.author("Phil B.")
.arg(Arg::new("username")
.help("Your Github username")
.action(ArgAction::Set)
.required(true))
.arg(Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::Count)
.help("Enable verbose output"))
.get_matches();
let username = matches.get_one::<String>("username").unwrap();
let verbose = matches.get_count("verbose");
let url = format!("https://api.github.com/users/{}/events", username);
match get_and_parse_json(&url, verbose) {
Ok(data) => look_for_events(data, verbose),
Err(MyError::Io(err)) => die!("IO error: {}", err),
Err(MyError::Ureq(err)) => die!("HTTP request error: {}", err),
}
}