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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//! Types related to iterations returned by the [Exercism website](https://exercism.org) v2 API.
//!
//! Solutions to exercises can have multiple iterations.
pub(crate) mod detail;
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, Display, IntoStaticStr};
use crate::api::v2::submission::analysis::{AnalyzerFeedback, RepresenterFeedback};
use crate::api::v2::{submission, tests};
/// Information about a specific iteration of a [`Solution`](crate::api::v2::solution::Solution)
/// submitted to the [Exercism website](https://exercism.org).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Iteration {
/// Iteration unique ID.
pub uuid: String,
/// Unique ID of the iteration's submission.
///
/// An iteration's submission is tied to the actual data sent for the iteration.
///
/// Will be `None` for deleted iterations.
#[serde(default)]
pub submission_uuid: Option<String>,
/// 1-based index of the iteration.
///
/// This is incremented every time an iteration is submitted for an exercise.
#[serde(rename = "idx")]
pub index: i32,
/// Iteration status.
pub status: Status,
/// Number of [essential](submission::analysis::AnalyzerCommentType::Essential) comments returned during this
/// iteration's submission's automated analysis.
pub num_essential_automated_comments: i32,
/// Number of [actionable](submission::analysis::AnalyzerCommentType::Actionable) comments returned during this
/// iteration's submission's automated analysis.
pub num_actionable_automated_comments: i32,
/// Number of [non-actionable](submission::analysis::AnalyzerCommentType::Informative) comments returned during this
/// iteration's submission's automated analysis.
///
/// # Notes
///
/// In the analyzer documentation, this level of feedback is known as "informative".
pub num_non_actionable_automated_comments: i32,
/// Number of [celebratory](submission::analysis::AnalyzerCommentType::Celebratory) comments returned during this
/// iteration's submission's automated analysis.
pub num_celebratory_automated_comments: i32,
/// How the iteration was submitted.
///
/// This is a free-form string, but is probably constrained to a few values currently.
/// Values seen so far:
///
/// | Value | Submission method |
/// |-------|-------------------|
/// | `cli` | [Exercism CLI] |
/// | `api` | Online editor |
///
/// [Exercism CLI]: https://exercism.org/docs/using/solving-exercises/working-locally
pub submission_method: String,
/// Date/time when the iteration was created, in ISO-8601 format.
pub created_at: String,
/// Status of this iteration's submission's test run.
pub tests_status: tests::Status,
/// Feedback provided by the track's [representer](https://exercism.org/docs/building/tooling/representers) for
/// this iteration.
///
/// Will be `None` if the representer did not provide any feedback (or if the track has no representer).
///
/// # Notes
///
/// This field is only filled if automated feedback is sideloaded, which is not currently possible with the
/// v2 API [`Client`](crate::api::v2::Client).
#[serde(default, deserialize_with = "detail::deserialize_optional_feedback")]
pub representer_feedback: Option<RepresenterFeedback>,
/// Feedback provided by the track's [analyzer](https://exercism.org/docs/building/tooling/analyzers) for
/// this iteration.
///
/// Will be `None` if the analyzer did not provide any feedback (or if the track has no analyzer).
///
/// # Notes
///
/// This field is only filled if automated feedback is sideloaded, which is not currently possible with the
/// v2 API [`Client`](crate::api::v2::Client).
#[serde(default, deserialize_with = "detail::deserialize_optional_feedback")]
pub analyzer_feedback: Option<AnalyzerFeedback>,
/// Whether this iteration has been published.
///
/// When a solution is published, multiple iterations can be published.
pub is_published: bool,
/// Whether this is the solution's latest iteration.
///
/// # Notes
///
/// This field is not sent by the v2 API for deleted iterations. Presumably, if the
/// last iteration of a solution is deleted, then the next-to-last will have `is_latest`
/// set to `true` in its stead.
#[serde(default)]
pub is_latest: bool,
/// Information about the iteration's submitted files, including their content.
///
/// # Notes
///
/// This field is only filled if files are sideloaded, which is not currently possible with the v2 API [`Client`](crate::api::v2::Client).
#[serde(default)]
pub files: Vec<submission::files::File>,
/// Collection of links pertaining to the iteration.
pub links: Links,
}
/// Possible status of a solution iteration submitted to the [Exercism website](https://exercism.org).
#[derive(
Debug,
Default,
Copy,
Clone,
PartialEq,
Eq,
Serialize,
Deserialize,
Display,
AsRefStr,
IntoStaticStr,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum Status {
/// Iteration has been submitted but has not been queued for testing yet.
///
/// # Notes
///
/// If a track does not have a test runner, iterations would presumably stay in this state forever.
#[default]
Untested,
/// Tests for this iteration have been queued, but have not finished executing.
Testing,
/// Tests for this iteration have failed.
TestsFailed,
/// Tests for this iteration have completed successfully; automated feedback analysis is in progress.
Analyzing,
/// Tests for this iteration have completed successfully and essential automated feedback has been provided.
///
/// For more information about types of automated feedback, see [this analyzer document](https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#type-optional).
EssentialAutomatedFeedback,
/// Tests for this iteration have completed successfully and actionable automated feedback has been provided.
///
/// For more information about types of automated feedback, see [this analyzer document](https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#type-optional).
ActionableAutomatedFeedback,
/// Tests for this iteration have completed successfully and celebratory automated feedback has been provided.
///
/// For more information about types of automated feedback, see [this analyzer document](https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#type-optional).
CelebratoryAutomatedFeedback,
/// Tests for this iteration have completed successfully and non-actionable automated feedback has been provided.
///
/// For more information about types of automated feedback, see [this analyzer document](https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md#type-optional).
///
/// # Notes
///
/// In the analyzer documentation, this level of feedback is known as [informative](submission::analysis::AnalyzerCommentType::Informative).
NonActionableAutomatedFeedback,
/// Tests for this iteration have completed successfully; no automated feedback was provided.
NoAutomatedFeedback,
/// This iteration has been deleted by the user.
Deleted,
/// Unknown status.
///
/// Included so that if new iteration statuses are introduced in the website API later,
/// this crate won't break (hopefully).
#[serde(skip_serializing, other)]
Unknown,
}
/// Links pertaining to an [Exercism](https://exercism.org) iteration returned by the v2 API.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Links {
/// URL of the iteration on the [Exercism website](https://exercism.org).
///
/// This URL is only valid for the user that submitted the iteration.
#[serde(rename = "self")]
pub self_path: String,
/// API URL that can be used to fetch the iteration's submission's automated feedback.
///
/// Will be `None` if the iteration has no automated feedback (if it is deleted, for example).
#[serde(default)]
pub automated_feedback: Option<String>,
/// API URL of the iteration. Performing an HTTP `DELETE` on this URL will delete the iteration.
///
/// Will be `None` if the iteration is already deleted.
#[serde(default)]
pub delete: Option<String>,
/// URL of the exercise on the [Exercism website](https://exercism.org).
///
/// For the user that submitted the iteration, this URL will point to their solution.
pub solution: String,
/// API URL of the iteration's submission's test run.
///
/// Will be `None` if the iteration has no test run (if it is deleted, for example).
#[serde(default)]
pub test_run: Option<String>,
/// API URL of the iteration's submission's files (with content).
///
/// Will be `None` for deleted iterations.
#[serde(default)]
pub files: Option<String>,
}