1use crate::{
4 grade::ValueMeta,
5 ids::{AcquisitionRequestId, ContentDigest},
6 judgment::{Derivation, Unresolved},
7 plan::BoundSource,
8 time::Timestamp,
9};
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
14#[serde(deny_unknown_fields)]
15pub struct PlanAcquisition {
16 pub step: String,
17 pub kind: AcquisitionKind,
18 pub derivation: Derivation,
19 #[serde(deserialize_with = "outcomes")]
20 #[schemars(length(max = 10))]
21 pub outcomes: Vec<AcquisitionPossibleOutcome>,
22 #[serde(deserialize_with = "calls")]
23 #[schemars(range(min = 1, max = 4096))]
24 pub max_calls: u64,
25 #[serde(deserialize_with = "sources")]
26 #[schemars(length(min = 1, max = 4))]
27 pub sources: Vec<AcquisitionBinding>,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
31#[serde(rename_all = "snake_case")]
32pub enum AcquisitionKind {
33 Acquire,
34 Widen,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
38#[serde(rename_all = "snake_case")]
39pub enum AcquisitionPossibleOutcome {
40 Resolved,
41 Skipped,
42 MissingEvidence,
43 StaleEvidence,
44 InvalidInput,
45 BackendUnavailable,
46 InvalidBackendOutput,
47 BudgetExhausted,
48 DeadlineExceeded,
49 InsufficientGrade,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
53#[serde(deny_unknown_fields)]
54pub struct AcquisitionBinding {
55 pub source: BoundSource,
56 pub operation: String,
57 pub map: BTreeMap<String, String>,
59 pub unmapped: Vec<String>,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
63#[serde(deny_unknown_fields)]
64pub struct AcquisitionEvidence {
65 pub step: String,
66 pub kind: AcquisitionKind,
67 pub outcome: AcquisitionOutcome,
68 pub stopped_by: AcquisitionStop,
69 pub truncated: u64,
70 pub request_constructed: bool,
71 #[serde(deserialize_with = "tiers")]
72 #[schemars(length(min = 1, max = 8))]
73 pub tiers: Vec<TierEvidence>,
74 pub items: Vec<AcquiredItemEvidence>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
78#[serde(rename_all = "snake_case", deny_unknown_fields)]
79pub enum AcquisitionOutcome {
80 Resolved,
81 Skipped { guard: String },
82 Unresolved(Unresolved),
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
86#[serde(rename_all = "snake_case")]
87pub enum AcquisitionStop {
88 None,
89 Exhausted,
90 StopCondition,
91 PageLimit,
92 ItemLimit,
93 TiersExhausted,
94 TierFailure,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
98#[serde(deny_unknown_fields)]
99pub struct TierEvidence {
100 pub name: String,
101 pub outcome: TierOutcome,
102 pub relaxation: u64,
103 pub source: String,
104 pub operation: String,
105 pub request_constructed: bool,
106 pub items_received: u64,
107 pub unique_items_added: u64,
108 pub duplicates: u64,
109 #[serde(deserialize_with = "pages")]
110 #[schemars(length(max = 64))]
111 pub pages: Vec<PageEvidence>,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
115#[serde(rename_all = "snake_case", deny_unknown_fields)]
116pub enum TierOutcome {
117 Ok,
118 Empty,
119 Failed { reason: Unresolved },
120 Skipped,
121 NotNeeded,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
125#[serde(deny_unknown_fields)]
126pub struct PageEvidence {
127 pub request: AcquisitionRequestId,
128 pub digest: PageDigest,
129 pub items: u64,
130 pub unique_items_added: u64,
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
134#[serde(rename_all = "snake_case", deny_unknown_fields)]
135pub enum PageDigest {
136 None,
137 Sha256(ContentDigest),
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
141#[serde(deny_unknown_fields)]
142pub struct AcquiredItemEvidence {
143 pub digest: ContentDigest,
144 pub meta: ValueMeta,
145 #[serde(
146 default,
147 skip_serializing_if = "Option::is_none",
148 deserialize_with = "crate::screening::present"
149 )]
150 #[schemars(with = "ItemOrigin")]
151 pub origin: Option<ItemOrigin>,
152}
153
154#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
155#[serde(deny_unknown_fields)]
156pub struct ItemOrigin {
157 pub tier: String,
158 pub tier_index: u32,
159 pub relaxation: u64,
160 pub source: String,
161 pub operation: String,
162 pub page: u32,
163 pub rank: u64,
164 pub as_of_ms: Timestamp,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
168#[serde(deny_unknown_fields)]
169pub struct AcquisitionRecord {
170 pub step: String,
171 pub tier: u32,
172 pub page: u32,
173 pub request: AcquisitionRequestId,
174 #[serde(deserialize_with = "attempts")]
175 #[schemars(length(max = 8))]
176 pub attempts: Vec<AcquisitionAttempt>,
177 pub cache: AcquisitionCache,
178 pub result: AcquisitionResult,
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
182#[serde(deny_unknown_fields)]
183pub struct AcquisitionAttempt {
184 pub attempt_id: String,
186 pub dispatched_ms: u64,
187 pub ended_ms: u64,
188 pub end: AcquisitionAttemptEnd,
189 pub cost: crate::run::AttemptCost,
190 pub cancellation: crate::run::Cancellation,
191 pub remote: crate::run::RemoteState,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
195#[serde(rename_all = "snake_case", deny_unknown_fields)]
196pub enum AcquisitionAttemptEnd {
197 Page,
198 InvalidPage,
199 Transient,
200 Overloaded,
201 Permanent,
202 Denied,
203 TimedOut,
204 AdapterFault,
205 Deadline,
206 Cancelled,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
210#[serde(rename_all = "snake_case", deny_unknown_fields)]
211pub enum AcquisitionCache {
212 None,
213 Hit { age_ms: u64 },
214 Stored,
215}
216
217#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
218#[serde(rename_all = "snake_case", deny_unknown_fields)]
219pub enum AcquisitionResult {
220 Page,
221 Failed(Unresolved),
222 NotSupplied,
223}
224
225pub(crate) fn list<'de, D: serde::Deserializer<'de>, T: Deserialize<'de>>(
226 d: D,
227) -> Result<Option<Vec<T>>, D::Error> {
228 bounded::<D, T, 1, 4096>(d).map(Some)
229}
230fn bounded<
231 'de,
232 D: serde::Deserializer<'de>,
233 T: Deserialize<'de>,
234 const MIN: usize,
235 const MAX: usize,
236>(
237 d: D,
238) -> Result<Vec<T>, D::Error> {
239 let v = Vec::<T>::deserialize(d)?;
240 if !(MIN..=MAX).contains(&v.len()) {
241 return Err(serde::de::Error::custom("acquisition list outside bounds"));
242 }
243 Ok(v)
244}
245fn outcomes<'de, D: serde::Deserializer<'de>>(
246 d: D,
247) -> Result<Vec<AcquisitionPossibleOutcome>, D::Error> {
248 bounded::<D, _, 0, 10>(d)
249}
250fn sources<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<AcquisitionBinding>, D::Error> {
251 bounded::<D, _, 1, 4>(d)
252}
253fn tiers<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<TierEvidence>, D::Error> {
254 bounded::<D, _, 1, 8>(d)
255}
256fn pages<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<PageEvidence>, D::Error> {
257 bounded::<D, _, 0, 64>(d)
258}
259fn attempts<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<AcquisitionAttempt>, D::Error> {
260 let v: Vec<AcquisitionAttempt> = bounded::<D, _, 0, 8>(d)?;
261 if v.iter().any(|a| a.ended_ms < a.dispatched_ms) {
262 return Err(serde::de::Error::custom("attempt ends before dispatch"));
263 }
264 Ok(v)
265}
266fn calls<'de, D: serde::Deserializer<'de>>(d: D) -> Result<u64, D::Error> {
267 let n = u64::deserialize(d)?;
268 if !(1..=4096).contains(&n) {
269 return Err(serde::de::Error::custom(
270 "acquisition calls outside 1..=4096",
271 ));
272 }
273 Ok(n)
274}
275pub(crate) fn constructed<'de, D: serde::Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
276 if !bool::deserialize(d)? {
277 return Err(serde::de::Error::custom("omit constructed when false"));
278 }
279 Ok(true)
280}
281
282pub(crate) fn constructed_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
283 schemars::json_schema!({"type":"boolean", "const":true})
284}