use super::MAX_PLAN_DEPENDENCIES;
use super::OperationPlanError;
use crate::model::artifacts::canonical_hash;
use crate::model::attempt_journal::AttemptBudgetRecord;
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Clone, Debug)]
pub struct PlannedOperationRequest {
pub operation_sequence: u64,
pub depends_on: Vec<u64>,
pub target: String,
pub request: String,
pub budget: AttemptBudgetRecord,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(try_from = "OperationFields")]
pub struct PlannedOperationRecord {
operation_sequence: u64,
depends_on: Vec<u64>,
target: String,
request: String,
budget: AttemptBudgetRecord,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct OperationFields {
operation_sequence: u64,
#[serde(deserialize_with = "bounded_dependencies")]
depends_on: Vec<u64>,
target: String,
request: String,
budget: AttemptBudgetRecord,
}
impl TryFrom<OperationFields> for PlannedOperationRecord {
type Error = OperationPlanError;
fn try_from(fields: OperationFields) -> Result<Self, Self::Error> {
Self::new(PlannedOperationRequest {
operation_sequence: fields.operation_sequence,
depends_on: fields.depends_on,
target: fields.target,
request: fields.request,
budget: fields.budget,
})
}
}
impl PlannedOperationRecord {
pub fn new(mut request: PlannedOperationRequest) -> Result<Self, OperationPlanError> {
if request.depends_on.len() > MAX_PLAN_DEPENDENCIES {
return Err(OperationPlanError::TooManyDependencies);
}
request.depends_on.sort_unstable();
for pair in request.depends_on.windows(2) {
if pair[0] == pair[1] {
return Err(OperationPlanError::DuplicateDependency {
operation_sequence: request.operation_sequence,
dependency: pair[0],
});
}
}
Ok(Self {
operation_sequence: request.operation_sequence,
depends_on: request.depends_on,
target: crate::model::principal::canonical_text(&request.target)
.ok_or(OperationPlanError::InvalidPrincipal("target"))?,
request: canonical_hash(&request.request)?,
budget: request.budget,
})
}
#[must_use]
pub const fn operation_sequence(&self) -> u64 {
self.operation_sequence
}
#[must_use]
pub fn depends_on(&self) -> &[u64] {
&self.depends_on
}
#[must_use]
pub fn target(&self) -> &str {
&self.target
}
#[must_use]
pub fn request(&self) -> &str {
&self.request
}
#[must_use]
pub const fn budget(&self) -> &AttemptBudgetRecord {
&self.budget
}
}
fn bounded_dependencies<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u64>, D::Error> {
crate::model::bounded_sequence::deserialize::<_, _, MAX_PLAN_DEPENDENCIES>(
deserializer,
"bounded exact operation dependencies",
OperationPlanError::TooManyDependencies,
)
}