use crate::{
grade::ValueMeta,
ids::{AcquisitionRequestId, ContentDigest},
judgment::{Derivation, Unresolved},
plan::BoundSource,
time::Timestamp,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PlanAcquisition {
pub step: String,
pub kind: AcquisitionKind,
pub derivation: Derivation,
#[serde(deserialize_with = "outcomes")]
#[schemars(length(max = 10))]
pub outcomes: Vec<AcquisitionPossibleOutcome>,
#[serde(deserialize_with = "calls")]
#[schemars(range(min = 1, max = 4096))]
pub max_calls: u64,
#[serde(deserialize_with = "sources")]
#[schemars(length(min = 1, max = 4))]
pub sources: Vec<AcquisitionBinding>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AcquisitionKind {
Acquire,
Widen,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AcquisitionPossibleOutcome {
Resolved,
Skipped,
MissingEvidence,
StaleEvidence,
InvalidInput,
BackendUnavailable,
InvalidBackendOutput,
BudgetExhausted,
DeadlineExceeded,
InsufficientGrade,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AcquisitionBinding {
pub source: BoundSource,
pub operation: String,
pub map: BTreeMap<String, String>,
pub unmapped: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AcquisitionEvidence {
pub step: String,
pub kind: AcquisitionKind,
pub outcome: AcquisitionOutcome,
pub stopped_by: AcquisitionStop,
pub truncated: u64,
pub request_constructed: bool,
#[serde(deserialize_with = "tiers")]
#[schemars(length(min = 1, max = 8))]
pub tiers: Vec<TierEvidence>,
pub items: Vec<AcquiredItemEvidence>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum AcquisitionOutcome {
Resolved,
Skipped { guard: String },
Unresolved(Unresolved),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AcquisitionStop {
None,
Exhausted,
StopCondition,
PageLimit,
ItemLimit,
TiersExhausted,
TierFailure,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct TierEvidence {
pub name: String,
pub outcome: TierOutcome,
pub relaxation: u64,
pub source: String,
pub operation: String,
pub request_constructed: bool,
pub items_received: u64,
pub unique_items_added: u64,
pub duplicates: u64,
#[serde(deserialize_with = "pages")]
#[schemars(length(max = 64))]
pub pages: Vec<PageEvidence>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum TierOutcome {
Ok,
Empty,
Failed { reason: Unresolved },
Skipped,
NotNeeded,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PageEvidence {
pub request: AcquisitionRequestId,
pub digest: PageDigest,
pub items: u64,
pub unique_items_added: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum PageDigest {
None,
Sha256(ContentDigest),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AcquiredItemEvidence {
pub digest: ContentDigest,
pub meta: ValueMeta,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "crate::screening::present"
)]
#[schemars(with = "ItemOrigin")]
pub origin: Option<ItemOrigin>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ItemOrigin {
pub tier: String,
pub tier_index: u32,
pub relaxation: u64,
pub source: String,
pub operation: String,
pub page: u32,
pub rank: u64,
pub as_of_ms: Timestamp,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AcquisitionRecord {
pub step: String,
pub tier: u32,
pub page: u32,
pub request: AcquisitionRequestId,
#[serde(deserialize_with = "attempts")]
#[schemars(length(max = 8))]
pub attempts: Vec<AcquisitionAttempt>,
pub cache: AcquisitionCache,
pub result: AcquisitionResult,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AcquisitionAttempt {
pub attempt_id: String,
pub dispatched_ms: u64,
pub ended_ms: u64,
pub end: AcquisitionAttemptEnd,
pub cost: crate::run::AttemptCost,
pub cancellation: crate::run::Cancellation,
pub remote: crate::run::RemoteState,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum AcquisitionAttemptEnd {
Page,
InvalidPage,
Transient,
Overloaded,
Permanent,
Denied,
TimedOut,
AdapterFault,
Deadline,
Cancelled,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum AcquisitionCache {
None,
Hit { age_ms: u64 },
Stored,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum AcquisitionResult {
Page,
Failed(Unresolved),
NotSupplied,
}
pub(crate) fn list<'de, D: serde::Deserializer<'de>, T: Deserialize<'de>>(
d: D,
) -> Result<Option<Vec<T>>, D::Error> {
bounded::<D, T, 1, 4096>(d).map(Some)
}
fn bounded<
'de,
D: serde::Deserializer<'de>,
T: Deserialize<'de>,
const MIN: usize,
const MAX: usize,
>(
d: D,
) -> Result<Vec<T>, D::Error> {
let v = Vec::<T>::deserialize(d)?;
if !(MIN..=MAX).contains(&v.len()) {
return Err(serde::de::Error::custom("acquisition list outside bounds"));
}
Ok(v)
}
fn outcomes<'de, D: serde::Deserializer<'de>>(
d: D,
) -> Result<Vec<AcquisitionPossibleOutcome>, D::Error> {
bounded::<D, _, 0, 10>(d)
}
fn sources<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<AcquisitionBinding>, D::Error> {
bounded::<D, _, 1, 4>(d)
}
fn tiers<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<TierEvidence>, D::Error> {
bounded::<D, _, 1, 8>(d)
}
fn pages<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<PageEvidence>, D::Error> {
bounded::<D, _, 0, 64>(d)
}
fn attempts<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<AcquisitionAttempt>, D::Error> {
let v: Vec<AcquisitionAttempt> = bounded::<D, _, 0, 8>(d)?;
if v.iter().any(|a| a.ended_ms < a.dispatched_ms) {
return Err(serde::de::Error::custom("attempt ends before dispatch"));
}
Ok(v)
}
fn calls<'de, D: serde::Deserializer<'de>>(d: D) -> Result<u64, D::Error> {
let n = u64::deserialize(d)?;
if !(1..=4096).contains(&n) {
return Err(serde::de::Error::custom(
"acquisition calls outside 1..=4096",
));
}
Ok(n)
}
pub(crate) fn constructed<'de, D: serde::Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
if !bool::deserialize(d)? {
return Err(serde::de::Error::custom("omit constructed when false"));
}
Ok(true)
}
pub(crate) fn constructed_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({"type":"boolean", "const":true})
}