Skip to main content

xposedornot/
models.rs

1//! Request and response types for the XposedOrNot API.
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5/// Deserializes `null` JSON values as an empty Vec.
6fn deserialize_null_as_empty_vec<'de, D, T>(deserializer: D) -> std::result::Result<Vec<T>, D::Error>
7where
8    D: Deserializer<'de>,
9    T: Deserialize<'de>,
10{
11    let opt: Option<Vec<T>> = Option::deserialize(deserializer)?;
12    Ok(opt.unwrap_or_default())
13}
14
15// ---------------------------------------------------------------------------
16// Email check responses
17// ---------------------------------------------------------------------------
18
19/// Response from the free email check endpoint (`/v1/check-email/{email}`).
20///
21/// The `breaches` field contains a nested array of breach names.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct FreeEmailCheckResponse {
24    /// Nested list of breach names associated with the email.
25    #[serde(default)]
26    pub breaches: Vec<Vec<String>>,
27}
28
29/// A single breach record returned by the Plus API.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct PlusBreachDetail {
32    /// Unique breach identifier.
33    pub breach_id: String,
34    /// Date the breach occurred.
35    pub breached_date: String,
36    /// Logo URL for the breached service.
37    pub logo: String,
38    /// Password risk level.
39    pub password_risk: String,
40    /// Whether the breach is searchable.
41    pub searchable: String,
42    /// Types of data exposed.
43    pub xposed_data: String,
44    /// Number of records exposed.
45    pub xposed_records: u64,
46    /// Description of the exposure.
47    pub xposure_desc: String,
48    /// Domain of the breached service.
49    pub domain: String,
50}
51
52/// Response from the Plus API email check endpoint (`/v3/check-email/{email}`).
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct PlusEmailCheckResponse {
55    /// Status of the request (e.g., "success").
56    pub status: String,
57    /// The email that was checked.
58    pub email: String,
59    /// Detailed breach records.
60    pub breaches: Vec<PlusBreachDetail>,
61}
62
63/// Unified email check result that covers both free and Plus API responses.
64#[derive(Debug, Clone)]
65pub enum EmailCheckResult {
66    /// Result from the free API.
67    Free(FreeEmailCheckResponse),
68    /// Result from the Plus API.
69    Plus(PlusEmailCheckResponse),
70}
71
72// ---------------------------------------------------------------------------
73// Breach listing
74// ---------------------------------------------------------------------------
75
76/// A single breach entry from the breach listing endpoint.
77#[derive(Debug, Clone, Serialize, Deserialize)]
78#[serde(rename_all = "camelCase")]
79pub struct BreachRecord {
80    /// Unique breach identifier.
81    #[serde(alias = "breachID")]
82    pub breach_id: String,
83    /// Date the breach occurred.
84    pub breached_date: String,
85    /// Domain of the breached service.
86    pub domain: String,
87    /// Industry category.
88    pub industry: String,
89    /// Types of data exposed (array of strings from the API).
90    pub exposed_data: Vec<String>,
91    /// Number of records exposed.
92    pub exposed_records: u64,
93    /// Whether the breach has been verified.
94    pub verified: bool,
95}
96
97/// Response from the breach listing endpoint (`/v1/breaches`).
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct BreachListResponse {
100    /// List of exposed breaches.
101    #[serde(rename = "exposedBreaches")]
102    pub exposed_breaches: Vec<BreachRecord>,
103}
104
105// ---------------------------------------------------------------------------
106// Breach analytics
107// ---------------------------------------------------------------------------
108
109/// Detailed information about a single breach in the analytics response.
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct BreachAnalyticsDetail {
112    /// The breach name or identifier.
113    #[serde(default)]
114    pub breach: Option<String>,
115    /// Additional fields are captured dynamically.
116    #[serde(flatten)]
117    pub extra: serde_json::Value,
118}
119
120/// Top-level container for exposed breaches in the analytics response.
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct ExposedBreaches {
123    /// List of breach detail objects.
124    #[serde(default)]
125    pub breaches_details: Vec<serde_json::Value>,
126}
127
128/// Summary of breaches in the analytics response.
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct BreachesSummary {
131    /// Dynamic summary fields.
132    #[serde(flatten)]
133    pub data: serde_json::Value,
134}
135
136/// Breach metrics in the analytics response.
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct BreachMetrics {
139    /// Dynamic metrics fields.
140    #[serde(flatten)]
141    pub data: serde_json::Value,
142}
143
144/// Pastes summary in the analytics response.
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct PastesSummary {
147    /// Dynamic summary fields.
148    #[serde(flatten)]
149    pub data: serde_json::Value,
150}
151
152/// Response from the breach analytics endpoint (`/v1/breach-analytics`).
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct BreachAnalyticsResponse {
155    /// Exposed breaches with details.
156    #[serde(rename = "ExposedBreaches")]
157    pub exposed_breaches: ExposedBreaches,
158    /// Summary of breaches.
159    #[serde(rename = "BreachesSummary")]
160    pub breaches_summary: BreachesSummary,
161    /// Breach metrics.
162    #[serde(rename = "BreachMetrics")]
163    pub breach_metrics: BreachMetrics,
164    /// Pastes summary.
165    #[serde(rename = "PastesSummary")]
166    pub pastes_summary: PastesSummary,
167    /// List of exposed pastes (null from API when no pastes found).
168    #[serde(rename = "ExposedPastes", default, deserialize_with = "deserialize_null_as_empty_vec")]
169    pub exposed_pastes: Vec<serde_json::Value>,
170}
171
172// ---------------------------------------------------------------------------
173// Password check
174// ---------------------------------------------------------------------------
175
176/// Inner result of the anonymous password search.
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct PasswordAnonResult {
179    /// The anonymous hash portion returned by the API.
180    pub anon: String,
181    /// Character composition breakdown (e.g., `"D:3;A:8;S:0;L:11"`).
182    pub char: String,
183    /// Number of times this password has been seen in breaches.
184    pub count: String,
185}
186
187/// Wrapper for the password search response.
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct PasswordSearchAnon {
190    /// The anonymous search result.
191    pub anon: String,
192    /// Character composition breakdown.
193    pub char: String,
194    /// Exposure count.
195    pub count: String,
196}
197
198/// Response from the password check endpoint (`/v1/pass/anon/{hash_prefix}`).
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct PasswordCheckResponse {
201    /// The anonymous password search result.
202    #[serde(rename = "SearchPassAnon")]
203    pub search_pass_anon: PasswordSearchAnon,
204}