use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Collection {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub requests: Vec<CollectionRequest>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub variables: HashMap<String, Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_profile: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CollectionRequest {
pub name: String,
pub operation: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<HashMap<String, Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub save_as: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expect: Option<Expectation>,
#[serde(
skip_serializing_if = "Option::is_none",
default,
deserialize_with = "deserialize_string_or_vec"
)]
pub depends_on: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "if")]
pub if_condition: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skip: Option<String>,
#[serde(default)]
pub run_always: bool,
#[serde(default)]
pub critical: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub retry: Option<RetryConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct RetryConfig {
pub attempts: u32,
pub delay: u64,
#[serde(default)]
pub backoff: BackoffStrategy,
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum BackoffStrategy {
Linear,
Exponential,
}
impl Default for BackoffStrategy {
fn default() -> Self {
BackoffStrategy::Linear
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Expectation {
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize)]
pub struct RequestResult {
pub name: String,
pub operation: String,
pub status: u16,
pub body: Option<Value>,
pub headers: HashMap<String, String>,
pub duration_ms: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub test_result: Option<TestResult>,
}
#[derive(Debug, Clone, Serialize)]
pub struct TestResult {
pub passed: bool,
pub assertions: Vec<AssertionResult>,
}
#[derive(Debug, Clone, Serialize)]
pub struct AssertionResult {
pub assertion_type: String,
pub passed: bool,
pub expected: Value,
pub actual: Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct CollectionSummary {
pub name: String,
pub total_requests: usize,
pub successful: usize,
pub failed: usize,
pub skipped: usize,
pub total_duration_ms: u64,
pub results: Vec<RequestResult>,
#[serde(skip_serializing_if = "Option::is_none")]
pub test_summary: Option<TestSummary>,
}
#[derive(Debug, Clone, Serialize)]
pub struct TestSummary {
pub total_tests: usize,
pub passed: usize,
pub failed: usize,
pub all_passed: bool,
}
fn deserialize_string_or_vec<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::{self, Visitor};
use std::fmt;
struct StringOrVecVisitor;
impl<'de> Visitor<'de> for StringOrVecVisitor {
type Value = Option<Vec<String>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string or a sequence of strings")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(None)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer
.deserialize_any(StringOrVecInnerVisitor)
.map(Some)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Some(vec![value.to_string()]))
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let vec = Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
Ok(Some(vec))
}
}
struct StringOrVecInnerVisitor;
impl<'de> Visitor<'de> for StringOrVecInnerVisitor {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string or a sequence of strings")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![value.to_string()])
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))
}
}
deserializer.deserialize_option(StringOrVecVisitor)
}