1use std::fmt;
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum TaskMode {
8 Async,
10 Sync,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16#[non_exhaustive]
17pub enum TaskType {
18 #[default]
20 RecaptchaV2TaskProxyless,
21 RecaptchaV2TaskProxylessS9,
23 RecaptchaV2STaskProxyless,
25 RecaptchaV2EnterpriseTaskProxyless,
27 RecaptchaV2SEnterpriseTaskProxyless,
29 RecaptchaV2Classification,
31 RecaptchaV3TaskProxyless,
33 RecaptchaV3TaskProxylessS9,
35 RecaptchaV3EnterpriseTaskProxyless,
37 RecaptchaV3EnterpriseTaskProxylessS9,
39 FuncaptchaTaskProxyless,
41 FuncaptchaClassification,
43 PerimeterX,
45 Hcaptcha,
47 HcaptchaClassification,
49 AkamaiWebTaskProxyless,
51 AkamaiSbsdTaskProxyless,
53 TlsTask,
55 Cloudflare5sTask,
57 CloudflareTurnstileTask,
59 DataDomeTaskProxyless,
61 DataDomeTagsTaskProxyless,
63 IncapsulaTaskProxyless,
65}
66
67impl TaskType {
68 pub const KNOWN: &'static [Self] = &[
70 Self::RecaptchaV2TaskProxyless,
71 Self::RecaptchaV2TaskProxylessS9,
72 Self::RecaptchaV2STaskProxyless,
73 Self::RecaptchaV2EnterpriseTaskProxyless,
74 Self::RecaptchaV2SEnterpriseTaskProxyless,
75 Self::RecaptchaV2Classification,
76 Self::RecaptchaV3TaskProxyless,
77 Self::RecaptchaV3TaskProxylessS9,
78 Self::RecaptchaV3EnterpriseTaskProxyless,
79 Self::RecaptchaV3EnterpriseTaskProxylessS9,
80 Self::FuncaptchaTaskProxyless,
81 Self::FuncaptchaClassification,
82 Self::PerimeterX,
83 Self::Hcaptcha,
84 Self::HcaptchaClassification,
85 Self::AkamaiWebTaskProxyless,
86 Self::AkamaiSbsdTaskProxyless,
87 Self::TlsTask,
88 Self::Cloudflare5sTask,
89 Self::CloudflareTurnstileTask,
90 Self::DataDomeTaskProxyless,
91 Self::DataDomeTagsTaskProxyless,
92 Self::IncapsulaTaskProxyless,
93 ];
94
95 #[must_use]
97 pub fn as_str(&self) -> &str {
98 match self {
99 Self::RecaptchaV2TaskProxyless => "ReCaptchaV2TaskProxyless",
100 Self::RecaptchaV2TaskProxylessS9 => "ReCaptchaV2TaskProxylessS9",
101 Self::RecaptchaV2STaskProxyless => "ReCaptchaV2STaskProxyless",
102 Self::RecaptchaV2EnterpriseTaskProxyless => "ReCaptchaV2EnterpriseTaskProxyless",
103 Self::RecaptchaV2SEnterpriseTaskProxyless => "ReCaptchaV2SEnterpriseTaskProxyless",
104 Self::RecaptchaV2Classification => "ReCaptchaV2Classification",
105 Self::RecaptchaV3TaskProxyless => "ReCaptchaV3TaskProxyless",
106 Self::RecaptchaV3TaskProxylessS9 => "ReCaptchaV3TaskProxylessS9",
107 Self::RecaptchaV3EnterpriseTaskProxyless => "ReCaptchaV3EnterpriseTaskProxyless",
108 Self::RecaptchaV3EnterpriseTaskProxylessS9 => "ReCaptchaV3EnterpriseTaskProxylessS9",
113 Self::FuncaptchaTaskProxyless => "FuncaptchaTaskProxyless",
114 Self::FuncaptchaClassification => "FunCaptchaClassification",
115 Self::PerimeterX => "PerimeterX",
116 Self::Hcaptcha => "HCaptcha",
117 Self::HcaptchaClassification => "HCaptchaClassification",
118 Self::AkamaiWebTaskProxyless => "AkamaiWEBTaskProxyless",
119 Self::AkamaiSbsdTaskProxyless => "AkamaiSBSDTaskProxyless",
120 Self::TlsTask => "TlsTask",
121 Self::Cloudflare5sTask => "CloudFlare5STask",
122 Self::CloudflareTurnstileTask => "CloudFlareTurnstileTask",
123 Self::DataDomeTaskProxyless => "DataDomeTaskProxyless",
124 Self::DataDomeTagsTaskProxyless => "DataDomeTagsTaskProxyless",
125 Self::IncapsulaTaskProxyless => "IncapsulaTaskProxyless",
126 }
127 }
128
129 #[must_use]
140 pub const fn mode(&self) -> Option<TaskMode> {
141 match self {
142 Self::RecaptchaV2TaskProxyless
143 | Self::RecaptchaV2TaskProxylessS9
144 | Self::RecaptchaV2STaskProxyless
145 | Self::RecaptchaV2EnterpriseTaskProxyless
146 | Self::RecaptchaV2SEnterpriseTaskProxyless
147 | Self::RecaptchaV3TaskProxyless
148 | Self::RecaptchaV3TaskProxylessS9
149 | Self::RecaptchaV3EnterpriseTaskProxyless
150 | Self::RecaptchaV3EnterpriseTaskProxylessS9
151 | Self::FuncaptchaTaskProxyless
152 | Self::PerimeterX
153 | Self::Hcaptcha
154 | Self::Cloudflare5sTask
155 | Self::CloudflareTurnstileTask => Some(TaskMode::Async),
156 Self::RecaptchaV2Classification
157 | Self::FuncaptchaClassification
158 | Self::HcaptchaClassification
159 | Self::AkamaiWebTaskProxyless
160 | Self::AkamaiSbsdTaskProxyless
161 | Self::TlsTask
162 | Self::DataDomeTaskProxyless
163 | Self::DataDomeTagsTaskProxyless
164 | Self::IncapsulaTaskProxyless => Some(TaskMode::Sync),
165 }
166 }
167
168 fn from_wire(value: &str) -> Option<Self> {
169 Self::KNOWN
170 .iter()
171 .find(|task_type| task_type.as_str().eq_ignore_ascii_case(value))
172 .copied()
173 }
174}
175
176pub trait TaskTypeName {
194 fn task_type_name(&self) -> &str;
196}
197
198impl TaskTypeName for TaskType {
199 fn task_type_name(&self) -> &str {
200 self.as_str()
201 }
202}
203
204impl TaskTypeName for str {
205 fn task_type_name(&self) -> &str {
206 self
207 }
208}
209
210impl TaskTypeName for String {
211 fn task_type_name(&self) -> &str {
212 self
213 }
214}
215
216impl<T> TaskTypeName for &T
217where
218 T: TaskTypeName + ?Sized,
219{
220 fn task_type_name(&self) -> &str {
221 (*self).task_type_name()
222 }
223}
224
225impl fmt::Display for TaskType {
226 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
227 formatter.write_str(self.as_str())
228 }
229}
230
231impl Serialize for TaskType {
232 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
233 where
234 S: Serializer,
235 {
236 serializer.serialize_str(self.as_str())
237 }
238}
239
240impl<'de> Deserialize<'de> for TaskType {
241 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
242 where
243 D: Deserializer<'de>,
244 {
245 let value = String::deserialize(deserializer)?;
246 Self::from_wire(&value)
247 .ok_or_else(|| D::Error::custom(format!("unknown task type `{value}`")))
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn known_type_deserialization_is_case_insensitive() -> Result<(), serde_json::Error> {
257 let task_type: TaskType = serde_json::from_str(r#""recaptchav2taskproxyless""#)?;
258 assert_eq!(task_type, TaskType::RecaptchaV2TaskProxyless);
259 assert_eq!(
260 serde_json::to_string(&task_type)?,
261 r#""ReCaptchaV2TaskProxyless""#
262 );
263 Ok(())
264 }
265
266 #[test]
267 fn a_plain_string_can_stand_in_for_a_task_type() {
268 assert_eq!("BrandNewTaskType".task_type_name(), "BrandNewTaskType");
270 assert_eq!(
271 TaskType::RecaptchaV2TaskProxyless.task_type_name(),
272 "ReCaptchaV2TaskProxyless"
273 );
274 }
275
276 #[test]
277 fn unknown_type_is_rejected() {
278 let error = serde_json::from_str::<TaskType>(r#""FutureTask""#).unwrap_err();
279 assert!(error.to_string().contains("unknown task type `FutureTask`"));
280 }
281}