use crate::{PlanError, PlanErrorCode, RefactorPlanLimits};
pub(super) struct TextBudget {
limits: RefactorPlanLimits,
total: usize,
}
impl TextBudget {
pub(super) const fn new(limits: RefactorPlanLimits) -> Self {
Self { limits, total: 0 }
}
pub(super) fn require_option(
&mut self,
value: Option<&str>,
field: &str,
) -> Result<(), PlanError> {
value.map_or_else(|| Err(missing(field)), |value| self.require(value, field))
}
pub(super) fn require(&mut self, value: &str, field: &str) -> Result<(), PlanError> {
if value.is_empty() {
return Err(invalid("evidence text must not be empty", field));
}
self.total = self
.total
.checked_add(value.len())
.ok_or_else(|| too_large("evidence text byte total overflow"))?;
if self.total > self.limits.max_evidence_text_bytes {
return Err(too_large("evidence text exceeds its combined byte limit").at_field(field));
}
Ok(())
}
pub(super) fn code(&mut self, value: &str, field: &str) -> Result<(), PlanError> {
if value.len() > self.limits.max_code_bytes {
return Err(too_large("evidence code exceeds its byte limit").at_field(field));
}
self.require(value, field)
}
}
pub(super) fn missing(field: &str) -> PlanError {
PlanError::new(
PlanErrorCode::EvidenceMissing,
format!("strict evidence requires {field}"),
)
.at_field(field)
}
pub(super) fn invalid(message: impl Into<String>, field: &str) -> PlanError {
PlanError::new(PlanErrorCode::EvidenceInvalid, message).at_field(field)
}
pub(super) fn too_large(message: impl Into<String>) -> PlanError {
PlanError::new(PlanErrorCode::EvidenceTooLarge, message)
}