use serde::{Deserialize, Serialize};
use std::net::IpAddr;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum ValidationState {
Speculative,
Confirmed,
Rejected,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct OriginCandidate {
pub ip: IpAddr,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
pub method: String,
pub confidence: u8,
pub validated: ValidationState,
}
impl OriginCandidate {
pub fn new(ip: IpAddr, method: impl Into<String>, confidence: u8) -> Self {
Self {
ip,
port: None,
method: method.into(),
confidence,
validated: ValidationState::Speculative,
}
}
pub fn new_with_port(ip: IpAddr, port: u16, method: impl Into<String>, confidence: u8) -> Self {
Self {
ip,
port: Some(port),
method: method.into(),
confidence,
validated: ValidationState::Speculative,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn origin_candidate_orders_by_fields() {
let low = OriginCandidate {
ip: "192.0.2.1".parse().unwrap(),
port: None,
method: "dns".into(),
confidence: 20,
validated: ValidationState::Speculative,
};
let high = OriginCandidate {
ip: "192.0.2.2".parse().unwrap(),
port: None,
method: "ssl".into(),
confidence: 90,
validated: ValidationState::Confirmed,
};
assert!(high > low);
}
#[test]
fn origin_candidate_serializes_cleanly() {
let candidate = OriginCandidate::new("192.0.2.10".parse().unwrap(), "http_header", 75);
let value = serde_json::to_value(candidate).unwrap();
assert_eq!(value["ip"], json!("192.0.2.10"));
assert_eq!(value["method"], json!("http_header"));
assert_eq!(value["confidence"], json!(75));
assert_eq!(value["validated"], json!("Speculative"));
}
}