Skip to main content

qa_spec/
answers.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use serde_cbor::{to_vec, value::to_value};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7/// Optional metadata paired with an `AnswerSet`.
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
9pub struct Meta {
10    #[serde(default, skip_serializing_if = "Option::is_none")]
11    pub created_at: Option<String>,
12    #[serde(default, skip_serializing_if = "Option::is_none")]
13    pub updated_at: Option<String>,
14}
15
16/// Represents in-progress answers for a given form spec version.
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
18pub struct AnswerSet {
19    pub form_id: String,
20    pub spec_version: String,
21    pub answers: Value,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub meta: Option<Meta>,
24}
25
26impl AnswerSet {
27    /// Creates a fresh empty answer set for a form.
28    pub fn new(form_id: impl Into<String>, spec_version: impl Into<String>) -> Self {
29        Self {
30            form_id: form_id.into(),
31            spec_version: spec_version.into(),
32            answers: Value::Object(Default::default()),
33            meta: None,
34        }
35    }
36
37    /// Serializes the answers set as canonical CBOR bytes.
38    pub fn to_cbor(&self) -> Result<Vec<u8>, serde_cbor::Error> {
39        let canonical = to_value(self)?;
40        to_vec(&canonical)
41    }
42
43    /// Serializes the answers set as indented JSON for debugging.
44    pub fn to_json_pretty(&self) -> Result<String, serde_json::Error> {
45        serde_json::to_string_pretty(self)
46    }
47}
48
49/// Progress tracking state for flows.
50#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
51pub struct ProgressState {
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub current_step: Option<String>,
54    pub completed: bool,
55    #[serde(default, skip_serializing_if = "Vec::is_empty")]
56    pub history: Vec<String>,
57}
58
59/// Validation error metadata reported by the engine.
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
61pub struct ValidationError {
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub question_id: Option<String>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub path: Option<String>,
66    pub message: String,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub code: Option<String>,
69    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
70    pub params: BTreeMap<String, String>,
71}
72
73/// Result returned from `validate_answers`.
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
75pub struct ValidationResult {
76    pub valid: bool,
77    #[serde(default, skip_serializing_if = "Vec::is_empty")]
78    pub errors: Vec<ValidationError>,
79    #[serde(default, skip_serializing_if = "Vec::is_empty")]
80    pub missing_required: Vec<String>,
81    #[serde(default, skip_serializing_if = "Vec::is_empty")]
82    pub unknown_fields: Vec<String>,
83}