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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use chrono::NaiveDate;
use reqwest::header;
use serde::{Deserialize, Serialize};
use serde_json::Number;
static BASE_API_URL: &str = "https://api.track.toggl.com/api/v9";
pub struct Client {
c: reqwest::blocking::Client,
token: String,
}
impl Client {
pub fn new(token: String) -> Result<Self, reqwest::Error> {
let mut headers = header::HeaderMap::new();
headers.insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
);
Ok(Client {
c: reqwest::blocking::Client::builder()
.default_headers(headers)
.build()?,
token,
})
}
pub fn get_time_entries(
&self,
start_end_dates: Option<(NaiveDate, NaiveDate)>,
) -> Result<Vec<TimeEntry>, reqwest::Error> {
let url = match start_end_dates {
Some((start_date, end_date)) => {
format!(
"{BASE_API_URL}/me/time_entries?start_date={start_date}&end_date={end_date}"
)
}
None => format!("{BASE_API_URL}/me/time_entries"),
};
self.c
.get(url)
.basic_auth(&self.token, Some("api_token"))
.send()?
.error_for_status()?
.json::<Vec<TimeEntry>>()
}
pub fn get_current_entry(&self) -> Result<Option<TimeEntry>, reqwest::Error> {
self.c
.get(format!("{BASE_API_URL}/me/time_entries/current"))
.basic_auth(&self.token, Some("api_token"))
.send()?
.error_for_status()?
.json()
}
pub fn create_time_entry(&self, entry: NewTimeEntry) -> Result<TimeEntry, reqwest::Error> {
let url = format!(
"{BASE_API_URL}/workspaces/{}/time_entries",
entry.workspace_id
);
self.c
.post(url)
.json(&entry)
.basic_auth(&self.token, Some("api_token"))
.send()?
.error_for_status()?
.json()
}
pub fn stop_time_entry(
&self,
workspace_id: &Number,
time_entry_id: &Number,
) -> Result<TimeEntry, reqwest::Error> {
let url =
format!("{BASE_API_URL}/workspaces/{workspace_id}/time_entries/{time_entry_id}/stop");
self.c
.patch(url)
.basic_auth(&self.token, Some("api_token"))
.send()?
.error_for_status()?
.json()
}
pub fn get_projects(&self, workspace_id: &Number) -> Result<Vec<Project>, reqwest::Error> {
self.c
.get(format!("{BASE_API_URL}/workspaces/{workspace_id}/projects"))
.basic_auth(&self.token, Some("api_token"))
.send()?
.error_for_status()?
.json()
}
pub fn get_workspaces(&self) -> Result<Vec<Workspace>, reqwest::Error> {
self.c
.get(format!("{BASE_API_URL}/workspaces"))
.basic_auth(&self.token, Some("api_token"))
.send()?
.error_for_status()?
.json()
}
}
#[derive(Deserialize, Debug)]
pub struct TimeEntry {
pub description: Option<String>,
pub duration: Number,
pub id: Number,
pub project_id: Option<Number>,
pub start: Option<String>,
pub stop: Option<String>,
pub task_id: Option<Number>,
pub workspace_id: Number,
}
#[derive(Serialize, Debug)]
pub struct NewTimeEntry {
pub created_with: String,
pub description: Option<String>,
pub duration: Number,
pub project_id: Option<Number>,
pub start: String,
pub stop: Option<String>,
pub task_id: Option<Number>,
pub workspace_id: Number,
}
#[derive(Deserialize, Debug)]
pub struct Project {
pub active: bool,
pub client_id: Option<Number>,
pub id: Number,
pub name: String,
pub workspace_id: Number,
}
#[derive(Deserialize, Debug)]
pub struct Workspace {
pub id: Number,
pub name: String,
}