gitlab_time_report/
model.rs

1//! Contains the data model for the time log data.
2
3use chrono::{DateTime, Duration, Local, NaiveDate};
4use serde::Deserialize;
5use serde_with::{DisplayFromStr, DurationSeconds, NoneAsEmptyString, serde_as};
6
7/// The queried GitLab repository.
8#[derive(Debug)]
9pub struct Project {
10    /// The name of the repository.
11    pub name: String,
12    /// The time logs of the repository.
13    pub time_logs: Vec<TimeLog>,
14    /// Total Time spent on the project
15    pub total_spent_time: Duration,
16}
17
18/// A single entry of time spent on an issue or merge request.
19#[serde_as]
20#[derive(Debug, PartialEq, Default, Deserialize)]
21#[serde(rename_all = "camelCase")]
22#[serde(rename = "Nodes")]
23pub struct TimeLog {
24    /// The date the time was spent. Is always set to a valid date in the API.
25    pub spent_at: DateTime<Local>,
26    /// The entered time that was spent.
27    #[serde_as(as = "DurationSeconds<i64>")]
28    pub time_spent: Duration,
29    /// The optional summary of what was done during the time.
30    /// Empty summaries are returned as empty strings by the GitLab API and turned into `None`.
31    #[serde_as(as = "NoneAsEmptyString")]
32    pub summary: Option<String>,
33    /// The user who spent the time.
34    pub user: User,
35    /// The Issue or Merge Request the time that was spent on.
36    #[serde(flatten)]
37    pub trackable_item: TrackableItem,
38}
39
40/// A list of GitLab users.
41#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, Deserialize)]
42pub struct UserNodes {
43    #[serde(rename = "nodes")]
44    pub users: Vec<User>,
45}
46
47/// The details of a GitLab user.
48#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Deserialize)]
49pub struct User {
50    pub name: String,
51    pub username: String,
52}
53
54impl std::fmt::Display for User {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{}", self.name)
57    }
58}
59
60/// A trackable item is either an issue or a merge request.
61#[derive(Debug, PartialEq, Default, Eq, Hash, PartialOrd, Ord, Clone)]
62pub struct TrackableItem {
63    /// Fields shared by all trackable items.
64    // Implementation note: The fields are in a separate struct to use serde attributes, as they
65    // are only supported when Deserialize is derived.
66    pub common: TrackableItemFields,
67    /// The type of the trackable item. Contains the fields that are only available for the specific type.
68    pub kind: TrackableItemKind,
69}
70
71/// The type of the trackable item. Each variant contains a struct with the fields that are
72/// only available for the specific type.
73#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
74pub enum TrackableItemKind {
75    Issue(Issue),
76    MergeRequest(MergeRequest),
77}
78
79impl Default for TrackableItemKind {
80    fn default() -> Self {
81        Self::Issue(Issue::default())
82    }
83}
84
85impl std::fmt::Display for TrackableItemKind {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match self {
88            TrackableItemKind::Issue(_) => write!(f, "Issue"),
89            TrackableItemKind::MergeRequest(_) => write!(f, "Merge Request"),
90        }
91    }
92}
93
94/// Contains the fields that are common to all trackable items.
95#[serde_as]
96#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Default, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct TrackableItemFields {
99    /// The issue or merge request ID.
100    #[serde(rename = "iid")]
101    #[serde_as(as = "DisplayFromStr")]
102    pub id: u32,
103    /// The title of the issue or merge request.
104    pub title: String,
105    /// The estimated time set for this item.
106    #[serde_as(as = "DurationSeconds<i64>")]
107    pub time_estimate: Duration,
108    /// The total time spent on this item.
109    #[serde_as(as = "DurationSeconds<i64>")]
110    pub total_time_spent: Duration,
111    /// The users assigned to this item. On the GitLab Free version, only one user can be assigned.
112    pub assignees: UserNodes,
113    /// The milestone assigned to this item.
114    pub milestone: Option<Milestone>,
115    /// The labels assigned to this item
116    pub labels: Labels,
117}
118
119/// Contains fields that are only available for issues (there are none).
120#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, Deserialize)]
121pub struct Issue {}
122
123/// Contains fields that are only available for merge requests.
124#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, Deserialize)]
125pub struct MergeRequest {
126    /// The users reviewing this MR. On the GitLab Free version, only one user can be assigned.
127    pub reviewers: UserNodes,
128}
129
130/// A milestone is a due date assigned to an issue or merge request.
131#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct Milestone {
134    /// The name of the milestone.
135    pub title: String,
136    /// When the milestone is due.
137    pub due_date: Option<NaiveDate>,
138}
139
140impl std::fmt::Display for Milestone {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        write!(f, "{}", self.title)
143    }
144}
145
146/// A list of labels assigned to an issue or merge request.
147#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, Deserialize)]
148pub struct Labels {
149    /// The labels assigned to this item.
150    #[serde(rename = "nodes")]
151    pub labels: Vec<Label>,
152}
153
154/// A single label assigned to an issue or merge request.
155#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, Deserialize)]
156pub struct Label {
157    /// The title of the label.
158    pub title: String,
159}
160
161impl std::fmt::Display for Label {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        write!(f, "{}", self.title)
164    }
165}