use crate::{
Answer, Answers, Error, ErrorKind, NamedQuestion, Operator, Provider, Request, State,
TypedQuestion, ask, operator,
};
#[derive(Clone, Debug)]
pub struct Step {
pub request: Request,
pub result: crate::Result<Answers>,
}
#[derive(Clone, Debug)]
pub enum FlowExit {
Undecided {
name: String,
answer: Box<Answer>,
},
Error(Error),
}
impl From<Error> for FlowExit {
fn from(error: Error) -> Self {
Self::Error(error)
}
}
#[derive(Clone, Debug)]
pub struct FlowAnswers {
answers: Answers,
}
impl FlowAnswers {
pub fn full(&self) -> &Answers {
&self.answers
}
pub fn peek<Q: TypedQuestion>(
&self,
operator: &Operator<Q>,
) -> crate::Result<Answer<Q::Output>> {
operator.answer(&self.answers)
}
pub fn require<Q: TypedQuestion>(&self, operator: &Operator<Q>) -> Result<Q::Output, FlowExit> {
let answer = self.peek(operator)?;
match answer.value() {
Some(value) => Ok(value.clone()),
None => Err(FlowExit::Undecided {
name: operator.name().into(),
answer: Box::new(
self.answers
.get(operator.name())
.ok_or_else(|| {
Error::new(ErrorKind::InvalidArgument, "operator not found")
})?
.revalidate(&operator.question().clone().into())?,
),
}),
}
}
}
#[derive(Clone, Debug)]
pub struct FlowOutcome<T> {
pub value: Option<T>,
pub stopped_at: Option<String>,
pub stopped_answer: Option<Answer>,
pub trace: Vec<Step>,
}
impl<T> FlowOutcome<T> {
pub fn decided(&self) -> bool {
self.stopped_at.is_none()
}
pub fn calls(&self) -> usize {
self.trace.len()
}
}
#[derive(Clone, Debug)]
pub struct FlowFailure {
pub error: Error,
pub trace: Vec<Step>,
}
impl std::fmt::Display for FlowFailure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.error.fmt(f)
}
}
impl std::error::Error for FlowFailure {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
pub struct FlowContext<'a> {
provider: &'a dyn Provider,
trace: Vec<Step>,
failure: Option<Error>,
}
impl<'a> FlowContext<'a> {
pub fn new(provider: &'a dyn Provider) -> Self {
Self {
provider,
trace: Vec::new(),
failure: None,
}
}
pub async fn ask(
&mut self,
state: &State,
operators: &[NamedQuestion],
) -> Result<FlowAnswers, FlowExit> {
let request = Request::new(state.clone(), operator::questions(operators)?)?;
self.ask_request(&request).await
}
pub async fn ask_request(&mut self, request: &Request) -> Result<FlowAnswers, FlowExit> {
if let Some(error) = &self.failure {
return Err(FlowExit::Error(error.clone()));
}
let result = ask(self.provider, request).await;
self.trace.push(Step {
request: request.clone(),
result: result.clone(),
});
match result {
Ok(answers) => Ok(FlowAnswers { answers }),
Err(error) => {
self.failure = Some(error.clone());
Err(FlowExit::Error(error))
}
}
}
pub fn finish<T>(self, result: Result<T, FlowExit>) -> Result<FlowOutcome<T>, FlowFailure> {
if let Some(error) = self.failure {
return Err(FlowFailure {
error,
trace: self.trace,
});
}
match result {
Ok(value) => Ok(FlowOutcome {
value: Some(value),
stopped_at: None,
stopped_answer: None,
trace: self.trace,
}),
Err(FlowExit::Undecided { name, answer }) => Ok(FlowOutcome {
value: None,
stopped_at: Some(name),
stopped_answer: Some(*answer),
trace: self.trace,
}),
Err(FlowExit::Error(error)) => Err(FlowFailure {
error,
trace: self.trace,
}),
}
}
}