use std::collections::BTreeSet;
use http::{Request, Uri};
use thiserror::Error;
use crate::model::{Effect, Gate, Operation};
use crate::request::{Invocation, ValueError};
use crate::values::Values;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Answers {
commit: bool,
gates: BTreeSet<String>,
}
impl Answers {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn commit(mut self) -> Self {
self.commit = true;
self
}
#[must_use]
pub fn gate(mut self, name: impl Into<String>) -> Self {
self.gates.insert(name.into());
self
}
#[must_use]
pub fn committed(&self) -> bool {
self.commit
}
#[must_use]
pub fn answered(&self, gate: &Gate) -> bool {
self.gates.contains(gate.as_str())
}
}
#[derive(Debug, Clone)]
pub enum Plan {
Send(Request<Vec<u8>>),
DryRun(Request<Vec<u8>>),
}
impl Plan {
#[must_use]
pub fn decide(op: &Operation, answers: &Answers, request: Request<Vec<u8>>) -> Self {
match (op.effect(), opened(op, answers)) {
(Effect::Read, _) | (Effect::Write, true) => Self::Send(request),
(Effect::Write, false) => Self::DryRun(request),
}
}
pub fn build(
op: &Operation,
base: &Uri,
values: Values,
answers: &Answers,
) -> Result<Self, PlanError> {
let invocation = Invocation::new(op, values)?;
Ok(Self::decide(op, answers, invocation.request(base)?))
}
#[must_use]
pub fn request(&self) -> &Request<Vec<u8>> {
match self {
Self::Send(request) | Self::DryRun(request) => request,
}
}
}
fn opened(op: &Operation, answers: &Answers) -> bool {
answers.committed() && op.gates().iter().all(|gate| answers.answered(gate))
}
#[derive(Debug, Error)]
pub enum PlanError {
#[error(transparent)]
Value(#[from] ValueError),
#[error("cannot build the request: {0}")]
Request(#[from] http::Error),
}