pub mod models;
use paste::paste;
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
use crate::models::*;
const API_BASE_URL: &str = "https://api.ouraring.com/v2/usercollection";
#[derive(Serialize, TypedBuilder)]
pub struct DateQuery<'a> {
#[builder(default = None, setter(strip_option))]
start_date: Option<&'a str>,
#[builder(default = None, setter(strip_option))]
end_date: Option<&'a str>,
#[builder(default = None, setter(strip_option))]
next_token: Option<&'a str>,
}
#[derive(Serialize, TypedBuilder)]
pub struct DatetimeQuery<'a> {
#[builder(default = None, setter(strip_option))]
start_datetime: Option<&'a str>,
#[builder(default = None, setter(strip_option))]
end_datetime: Option<&'a str>,
#[builder(default = None, setter(strip_option))]
next_token: Option<&'a str>,
}
#[derive(Deserialize, Debug, PartialEq)]
pub struct ListResponse<T> {
pub data: Vec<T>,
pub next_token: Option<String>,
}
macro_rules! generic_endpoint {
($(#[$m:meta])*, $name: ident, $type: ty, $path: literal) => {
$(#[$m])*
pub fn $name(&self) -> Result<$type, reqwest::Error> {
let url = format!("{}/{}", &self.base_url, $path);
let response = self
.client
.get(&url)
.bearer_auth(&self.token)
.send()?
.json::<$type>()?;
Ok(response)
}
};
}
macro_rules! list_endpoint {
($(#[$m:meta])*, $name: ident, $type: ty, $path: literal, $query: ty) => {
$(#[$m])*
pub fn $name(&self, query: $query) -> Result<ListResponse<$type>, reqwest::Error> {
let url = format!("{}/{}", &self.base_url, $path);
let response = self
.client
.get(&url)
.bearer_auth(&self.token)
.query(&query)
.send()?
.json::<ListResponse<$type>>()?;
Ok(response)
}
};
}
macro_rules! get_endpoint {
($(#[$m:meta])*, $name: ident, $type: ty, $path: literal) => {
$(#[$m])*
pub fn $name(&self, id: &str) -> Result<$type, reqwest::Error> {
let url = format!("{}/{}/{}", &self.base_url, $path, id);
let response = self
.client
.get(&url)
.bearer_auth(&self.token)
.send()?
.json::<$type>()?;
Ok(response)
}
};
}
macro_rules! endpoint_set {
($name: ident, $type: ty, $path: literal, $params: ty) => {
paste! {
get_endpoint!(#[doc = "Gets a single [" $type "] item by id."], [<get_ $name>], $type, $path);
list_endpoint!(#[doc = "Returns a [ListResponse] of [" $type "] items based on the supplied query."], [<list_ $name>], $type, $path, $params);
}
};
}
pub struct OuraClient<'a> {
token: &'a str,
base_url: &'a str,
client: Client,
}
impl<'a> OuraClient<'a> {
pub fn new(token: &'a str) -> Self {
let client = Client::new();
Self {
token,
client,
base_url: API_BASE_URL,
}
}
pub fn build_with_base_url(token: &'a str, base_url: &'a str) -> Self {
let client = Client::new();
Self {
token,
client,
base_url,
}
}
endpoint_set!(daily_activity, DailyActivity, "daily_activity", DateQuery);
endpoint_set!(
daily_readiness,
DailyReadiness,
"daily_readiness",
DateQuery
);
endpoint_set!(daily_sleep, DailySleep, "daily_sleep", DateQuery);
endpoint_set!(daily_spo2, DailySpO2, "daily_spo2", DateQuery);
list_endpoint!(#[doc = "Returns a [ListResponse] of [HeartRate] items based on the supplied query."], list_heart_rate, HeartRate, "heartrate", DatetimeQuery);
generic_endpoint!(#[doc = "Returns a [PersonalInfo] based on the caller."], get_personal_info, PersonalInfo, "personal_info");
endpoint_set!(
rest_mode_period,
RestModePeriod,
"rest_mode_period",
DateQuery
);
endpoint_set!(
ring_configuration,
RingConfiguration,
"ring_configuration",
DateQuery
);
endpoint_set!(session, Session, "session", DateQuery);
endpoint_set!(sleep, Sleep, "sleep", DateQuery);
endpoint_set!(sleep_time, SleepTime, "sleep_time", DateQuery);
endpoint_set!(tag, Tag, "tag", DateQuery);
endpoint_set!(workout, Workout, "workout", DateQuery);
endpoint_set!(tag_v2, TagV2, "tag/v2", DateQuery);
}