standout_input/questionnaire/
derive.rs1use super::{
2 Answers, FormError, Item, Questionnaire, QuestionnaireError, RawAnswers, ValidationDiagnostic,
3};
4
5pub trait QuestionnaireChoices:
6 Sized + std::str::FromStr<Err = QuestionnaireChoiceParseError> + std::fmt::Display
7{
8 fn choices() -> &'static [&'static str];
9}
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct QuestionnaireChoiceParseError {
13 choices: &'static [&'static str],
14}
15
16impl QuestionnaireChoiceParseError {
17 pub const fn new(choices: &'static [&'static str]) -> Self {
18 Self { choices }
19 }
20
21 pub fn choices(&self) -> &'static [&'static str] {
22 self.choices
23 }
24}
25
26impl std::fmt::Display for QuestionnaireChoiceParseError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "expected one of: {}", self.choices.join(", "))
29 }
30}
31
32impl std::error::Error for QuestionnaireChoiceParseError {}
33
34pub trait QuestionnaireInput: Sized {
35 fn questionnaire() -> Result<Questionnaire, QuestionnaireError>;
36
37 #[doc(hidden)]
38 fn from_decoded_answers(answers: &Answers) -> Self;
39
40 #[doc(hidden)]
41 fn questionnaire_items(prefix: &str) -> Vec<Item> {
42 assert!(
43 prefix.is_empty(),
44 "manual QuestionnaireInput implementation cannot be nested unless questionnaire_items is overridden"
45 );
46 Self::questionnaire()
47 .expect("manual QuestionnaireInput implementation cannot be nested unless questionnaire_items is overridden")
48 .items()
49 .to_vec()
50 }
51
52 #[doc(hidden)]
53 fn from_decoded_answers_at(answers: &Answers, prefix: &str) -> Self {
54 assert!(
55 prefix.is_empty(),
56 "manual QuestionnaireInput implementation cannot be nested unless from_decoded_answers_at is overridden"
57 );
58 Self::from_decoded_answers(answers)
59 }
60
61 fn from_raw_answers(raw: &RawAnswers) -> Result<Self, QuestionnaireInputError> {
62 let questionnaire = Self::questionnaire().map_err(QuestionnaireInputError::Definition)?;
63 let answers = questionnaire
64 .decode_answers(raw)
65 .map_err(QuestionnaireInputError::Validation)?;
66 Ok(Self::from_decoded_answers(&answers))
67 }
68
69 fn from_raw_answers_with<F>(raw: &RawAnswers, form: F) -> Result<Self, QuestionnaireInputError>
70 where
71 F: FnOnce(&Self) -> Vec<FormError>,
72 {
73 let questionnaire = Self::questionnaire().map_err(QuestionnaireInputError::Definition)?;
74 let answers = questionnaire
75 .decode_answers(raw)
76 .map_err(QuestionnaireInputError::Validation)?;
77 let value = Self::from_decoded_answers(&answers);
78 let form_errors = form(&value);
79 if form_errors.is_empty() {
80 return Ok(value);
81 }
82 Err(QuestionnaireInputError::Validation(
83 form_errors
84 .into_iter()
85 .map(|error| ValidationDiagnostic::Form {
86 fields: error.fields,
87 message: error.message,
88 })
89 .collect(),
90 ))
91 }
92}
93
94#[derive(Debug, thiserror::Error, PartialEq, Eq)]
95pub enum QuestionnaireInputError {
96 #[error("derived questionnaire definition is invalid: {0}")]
97 Definition(QuestionnaireError),
98
99 #[error("derived questionnaire answers are invalid: {}", validation_diagnostics_display(.0))]
100 Validation(Vec<ValidationDiagnostic>),
101}
102
103fn validation_diagnostics_display(diagnostics: &[ValidationDiagnostic]) -> String {
104 if diagnostics.is_empty() {
105 return "no diagnostics reported".to_string();
106 }
107
108 diagnostics
109 .iter()
110 .map(ToString::to_string)
111 .collect::<Vec<_>>()
112 .join("; ")
113}