Skip to main content

gingr/endpoint/
labor_ops.rs

1use super::{Date, Error, LocationId, Method, Request, Result};
2
3fn push_optional<T: core::fmt::Display>(
4    params: &mut Vec<(String, String)>,
5    key: &str,
6    value: Option<T>,
7) {
8    if let Some(value) = value {
9        params.push((key.to_owned(), value.to_string()));
10    }
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14/// Gingr user identifier accepted by labor-operation endpoints.
15pub struct UserId(u64);
16
17impl UserId {
18    /// Constructs this typed Gingr boundary value after the caller has chosen the provider input to trust.
19    pub fn new(value: u64) -> Self {
20        Self(value)
21    }
22}
23
24impl core::fmt::Display for UserId {
25    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26        write!(formatter, "{}", self.0)
27    }
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
31/// Typed request for Gingr timeclock reports used as labor workflow evidence.
32pub struct TimeclockReport {
33    start_date: Date,
34    end_date: Date,
35    location_id: LocationId,
36    include_deleted: Option<bool>,
37    include_clocked_in: Option<bool>,
38    user_ids: Vec<UserId>,
39}
40
41impl TimeclockReport {
42    /// Starts a builder that makes each provider parameter explicit before request capture.
43    pub fn builder() -> TimeclockReportBuilder {
44        TimeclockReportBuilder::default()
45    }
46}
47
48#[derive(Clone, Debug, Default)]
49/// Builder for Gingr timeclock report filters.
50pub struct TimeclockReportBuilder {
51    start_date: Option<Date>,
52    end_date: Option<Date>,
53    location_id: Option<LocationId>,
54    include_deleted: Option<bool>,
55    include_clocked_in: Option<bool>,
56    user_ids: Vec<UserId>,
57}
58
59impl TimeclockReportBuilder {
60    /// Scopes the labor report to a provider date range.
61    pub fn date_range(mut self, start_date: Date, end_date: Date) -> Self {
62        self.start_date = Some(start_date);
63        self.end_date = Some(end_date);
64        self
65    }
66
67    /// Scopes the Gingr endpoint request to a location.
68    pub fn location_id(mut self, location_id: LocationId) -> Self {
69        self.location_id = Some(location_id);
70        self
71    }
72
73    /// Includes deleted provider records when Gingr supports that filter.
74    pub fn include_deleted(mut self, include_deleted: bool) -> Self {
75        self.include_deleted = Some(include_deleted);
76        self
77    }
78
79    /// Includes currently clocked-in users in the labor report.
80    pub fn include_clocked_in(mut self, include_clocked_in: bool) -> Self {
81        self.include_clocked_in = Some(include_clocked_in);
82        self
83    }
84
85    /// Filters the labor report to one Gingr user.
86    pub fn user_id(mut self, user_id: UserId) -> Self {
87        self.user_ids.push(user_id);
88        self
89    }
90
91    /// Finalizes the provider request descriptor after required fields are present and wrappers have validated local invariants.
92    pub fn build(self) -> Result<TimeclockReport> {
93        Ok(TimeclockReport {
94            start_date: self.start_date.ok_or(Error::MissingRequiredParameter {
95                parameter: "start_date",
96            })?,
97            end_date: self.end_date.ok_or(Error::MissingRequiredParameter {
98                parameter: "end_date",
99            })?,
100            location_id: self.location_id.ok_or(Error::MissingRequiredParameter {
101                parameter: "location_id",
102            })?,
103            include_deleted: self.include_deleted,
104            include_clocked_in: self.include_clocked_in,
105            user_ids: self.user_ids,
106        })
107    }
108}
109
110impl Request for TimeclockReport {
111    fn method(&self) -> Method {
112        Method::Get
113    }
114
115    fn path(&self) -> &'static str {
116        "/api/v1/timeclock_report"
117    }
118
119    fn parameters(&self) -> Vec<(String, String)> {
120        let mut params = vec![
121            ("start_date".to_owned(), self.start_date.to_string()),
122            ("end_date".to_owned(), self.end_date.to_string()),
123            ("location_id".to_owned(), self.location_id.to_string()),
124        ];
125        push_optional(&mut params, "include_deleted", self.include_deleted);
126        push_optional(&mut params, "include_clocked_in", self.include_clocked_in);
127        params.extend(
128            self.user_ids
129                .iter()
130                .map(|user_id| ("user_ids[]".to_owned(), user_id.to_string())),
131        );
132        params
133    }
134}