use std::collections::BTreeMap;
use openssl::hash::{MessageDigest, hash};
use serde::Serialize;
use serde_json::{Value, json};
use crate::{TResult, crypto::Crypto};
#[derive(Debug, Serialize)]
pub(crate) struct RequestPlan {
url: String,
payload: Value,
cookies: BTreeMap<String, String>,
encryption: Crypto,
api_path: Option<String>,
}
pub(crate) struct RequestParts {
pub(crate) url: String,
pub(crate) payload: Value,
pub(crate) cookies: BTreeMap<String, String>,
pub(crate) encryption: Crypto,
pub(crate) api_path: Option<String>,
}
pub(crate) struct RequestPlanBuilder(RequestPlan);
impl RequestPlanBuilder {
pub(crate) fn post(url: impl Into<String>) -> Self {
Self(RequestPlan {
url: url.into(),
payload: json!({}),
cookies: BTreeMap::new(),
encryption: Crypto::Weapi,
api_path: None,
})
}
pub(crate) fn payload(mut self, payload: Value) -> Self {
self.0.payload = payload;
self
}
pub(crate) fn cookie(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.0.cookies.insert(name.into(), value.into());
self
}
pub(crate) fn encryption(mut self, encryption: Crypto) -> Self {
self.0.encryption = encryption;
self
}
pub(crate) fn api_path(mut self, path: impl Into<String>) -> Self {
self.0.api_path = Some(path.into());
self
}
pub(crate) fn build(self) -> RequestPlan {
self.0
}
}
impl RequestPlan {
pub(crate) fn id(&self) -> TResult<String> {
let serialized = serde_json::to_vec(self)?;
Ok(hex::encode(hash(MessageDigest::md5(), &serialized)?))
}
pub(crate) fn into_parts(self) -> RequestParts {
RequestParts {
url: self.url,
payload: self.payload,
cookies: self.cookies,
encryption: self.encryption,
api_path: self.api_path,
}
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::RequestPlanBuilder;
use crate::crypto::Crypto;
#[test]
fn request_identity_is_stable_for_equal_plans() {
let first = RequestPlanBuilder::post("https://example.test/api/search")
.payload(json!({"keywords": "mota"}))
.cookie("sid", "value")
.encryption(Crypto::Eapi)
.api_path("/api/search")
.build();
let second = RequestPlanBuilder::post("https://example.test/api/search")
.payload(json!({"keywords": "mota"}))
.cookie("sid", "value")
.encryption(Crypto::Eapi)
.api_path("/api/search")
.build();
assert_eq!(first.id().unwrap(), second.id().unwrap());
}
}