use std::collections::{BTreeMap, HashMap};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
use serde_json::Value;
use crate::{Error, Result};
#[derive(Debug, Clone, PartialEq, Default)]
pub enum Solution {
#[default]
Missing,
Value(Value),
}
impl Solution {
#[must_use]
pub const fn is_missing(&self) -> bool {
matches!(self, Self::Missing)
}
#[must_use]
pub const fn as_value(&self) -> Option<&Value> {
match self {
Self::Missing => None,
Self::Value(value) => Some(value),
}
}
#[must_use]
pub fn into_value(self) -> Option<Value> {
match self {
Self::Missing => None,
Self::Value(value) => Some(value),
}
}
pub fn deserialize<T>(&self) -> Result<T>
where
T: DeserializeOwned,
{
let value = self.as_value().ok_or_else(|| {
Error::UnexpectedResponse("ready task result does not contain a solution".to_owned())
})?;
T::deserialize(value).map_err(|source| Error::SolutionDecode {
source,
raw: Box::new(value.clone()),
})
}
}
impl Serialize for Solution {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Missing => serializer.serialize_none(),
Self::Value(value) => value.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for Solution {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Value::deserialize(deserializer).map(Self::Value)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct RecaptchaSolution {
#[serde(rename = "gRecaptchaResponse")]
pub token: String,
#[serde(default)]
pub sec_ch_ua: String,
#[serde(default)]
pub user_agent: String,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct FunCaptchaSolution {
pub token: String,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(remote = "Self")]
pub struct FunCaptchaClassificationSolution {
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(remote = "Self")]
pub struct HcaptchaClassificationSolution {
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct CloudflareTurnstileSolution {
pub token: String,
#[serde(default)]
pub header: BTreeMap<String, String>,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self", rename_all = "camelCase")]
pub struct Cloudflare5sSolution {
#[serde(default)]
pub header: BTreeMap<String, String>,
#[serde(default)]
pub cookies: BTreeMap<String, String>,
#[serde(default)]
pub tls_version: String,
#[serde(default)]
pub body: String,
#[serde(default)]
pub s_token: String,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct HcaptchaSolution {
#[serde(rename = "generated_pass_UUID")]
pub generated_pass_uuid: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ua: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lang: Option<String>,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct PerimeterXSolution {
#[serde(rename = "_px3")]
pub px3: String,
#[serde(rename = "_pxvid", default)]
pub pxvid: String,
#[serde(rename = "_pxde", default)]
pub pxde: String,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct AkamaiWebSolution {
pub payload: String,
#[serde(default)]
pub encodedata: String,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct AkamaiSbsdSolution {
pub payload: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bm_lso_time: Option<String>,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct TlsForwardSolution {
#[serde(default)]
pub status: u16,
#[serde(default)]
pub code: u16,
#[serde(default)]
pub headers: BTreeMap<String, Value>,
#[serde(default)]
pub cookies: HashMap<String, Value>,
#[serde(default)]
pub body: String,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(remote = "Self", default, rename_all = "camelCase")]
pub struct ReClassificationSolution {
pub r#type: String,
pub has_object: bool,
pub objects: Vec<usize>,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
impl ReClassificationSolution {
#[must_use]
pub fn is_multi(&self) -> bool {
self.r#type == "multi"
}
#[must_use]
pub fn is_single(&self) -> bool {
self.r#type == "single"
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct DataDomeSolution {
#[serde(default)]
pub kind: Option<String>,
#[serde(default)]
pub url: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(remote = "Self")]
pub struct IncapsulaSolution {
#[serde(default)]
pub status: Option<u16>,
pub data: String,
#[serde(flatten, skip_serializing)]
pub extra: BTreeMap<String, Value>,
}
macro_rules! impl_solution_serde {
($($ty:ident),+ $(,)?) => {$(
impl Serialize for $ty {
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
crate::wire::merge_extra(|s| <$ty>::serialize(self, s), &self.extra, serializer)
}
}
impl<'de> Deserialize<'de> for $ty {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
<$ty>::deserialize(deserializer)
}
}
)+};
}
impl_solution_serde!(
AkamaiSbsdSolution,
AkamaiWebSolution,
Cloudflare5sSolution,
CloudflareTurnstileSolution,
DataDomeSolution,
FunCaptchaClassificationSolution,
FunCaptchaSolution,
HcaptchaClassificationSolution,
HcaptchaSolution,
IncapsulaSolution,
PerimeterXSolution,
ReClassificationSolution,
RecaptchaSolution,
TlsForwardSolution,
);