wakapi 0.3.1

Wakatime API client
Documentation
//! All time since today endpoint.
//!
//! - Ref : <https://wakatime.com/developers#all_time_since_today>
//! - Last checked : 2026-05-15

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use crate::{client::WakapiClient, error::WakapiError};

/// Request parameters for the AllTimeSinceToday endpoint.
#[derive(Serialize, Default)]
pub struct AllTimeSinceTodayParams {
    project: Option<String>,
}

impl AllTimeSinceTodayParams {
    /// Create a new AllTimeSinceTodayRequestParams with default values (no project).
    pub fn new() -> AllTimeSinceTodayParams {
        AllTimeSinceTodayParams { project: None }
    }

    /// Set the project parameter for the request.
    pub fn project(mut self, project: &str) -> AllTimeSinceTodayParams {
        self.project = Some(project.to_string());
        self
    }

    /// Clear the project parameter for the request.
    pub fn no_project(mut self) -> AllTimeSinceTodayParams {
        self.project = None;
        self
    }
}

/// All time since today endpoint body
///
/// Ref : <https://wakatime.com/developers#all_time_since_today>
///
#[derive(Deserialize, Debug)]
pub struct AllTimeSinceTodayBody {
    pub data: AllTimeSinceTodayData,
}

#[derive(Deserialize, Debug)]
pub struct AllTimeSinceTodayData {
    /// average coding activity per day as seconds for the given range of time, including Other language
    pub daily_average: f64,
    /// total coding activity in decimal format
    pub decimal: String,
    /// total coding activity in digital clock format
    pub digital: String,
    /// true if the stats are up to date; when false, a 202 response code is returned and stats will be refreshed soon
    pub is_up_to_date: bool,
    /// a number between 0 and 100 where 100 means the stats are up to date including Today’s time
    pub percent_calculated: u8,

    /// range of time for the stats
    pub range: AllTimeSinceTodayRange,

    /// total time logged since account created as human readable string
    pub text: String,
    /// keystroke timeout setting in minutes
    pub timeout: usize,
    /// total number of seconds logged since account created
    pub total_seconds: f64,
}

#[derive(Deserialize, Debug)]
pub struct AllTimeSinceTodayRange {
    /// end of today as ISO 8601 UTC datetime
    pub end: DateTime<Utc>,
    /// today as Date string in YEAR-MONTH-DAY format
    pub end_date: String,
    /// today in human-readable format
    pub end_text: String,

    /// start of user created day as ISO 8601 UTC datetime
    pub start: DateTime<Utc>,
    /// user created day as Date string in YEAR-MONTH-DAY format
    pub start_date: String,
    /// user created day in human-readable format
    pub start_text: String,

    /// timezone used in Olson Country/Region format
    pub timezone: String,
}

/// Successful response from the AllTimeSinceToday endpoint, with two possible states :
/// - Success : the stats are up to date, 200 response code
/// - WillRefresh : the stats are not up to date, 202 response code
#[derive(Debug)]
pub enum AllTimeSinceToday {
    /// The stats are up to date, 200 response code
    Success(AllTimeSinceTodayBody),
    /// The stats are not up to date, 202 response code
    WillRefresh(AllTimeSinceTodayBody),
}

impl AllTimeSinceToday {
    #[cfg(feature = "blocking")]
    pub fn fetch(
        client: &WakapiClient,
        params: AllTimeSinceTodayParams,
    ) -> Result<AllTimeSinceToday, WakapiError> {
        let url_params = serde_url_params::to_string(&params)?;
        let url = client.build_url("/api/v1/users/:user/all_time_since_today", Some(url_params));

        let response = reqwest::blocking::Client::new()
            .get(&url)
            .header("Authorization", client.get_auth_header())
            .send()?;
        match response.status() {
            reqwest::StatusCode::OK => {
                let data = response.json::<AllTimeSinceTodayBody>()?;
                Ok(AllTimeSinceToday::Success(data))
            }
            reqwest::StatusCode::ACCEPTED => {
                let data = response.json::<AllTimeSinceTodayBody>()?;
                Ok(AllTimeSinceToday::WillRefresh(data))
            }
            _ => Err(WakapiError::RequestError(
                response.error_for_status().unwrap_err(),
            )),
        }
    }

    #[cfg(not(feature = "blocking"))]
    pub async fn fetch(
        client: &WakapiClient,
        params: AllTimeSinceTodayParams,
    ) -> Result<AllTimeSinceToday, WakapiError> {
        let url_params = serde_url_params::to_string(&params)?;
        let url = client.build_url("/api/v1/users/:user/all_time_since_today", Some(url_params));

        let response = reqwest::Client::new()
            .get(&url)
            .header("Authorization", client.get_auth_header())
            .send()
            .await?;
        match response.status() {
            reqwest::StatusCode::OK => {
                let data = response.json::<AllTimeSinceTodayBody>().await?;
                Ok(AllTimeSinceToday::Success(data))
            }
            reqwest::StatusCode::ACCEPTED => {
                let data = response.json::<AllTimeSinceTodayBody>().await?;
                Ok(AllTimeSinceToday::WillRefresh(data))
            }
            _ => Err(WakapiError::RequestError(
                response.error_for_status().unwrap_err(),
            )),
        }
    }
}