1use std::collections::HashMap;
2
3use crate::error::VldError;
4
5#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
10pub struct FlatError {
11 pub form_errors: Vec<String>,
13 pub field_errors: HashMap<String, Vec<String>>,
15}
16
17pub fn flatten_error(error: &VldError) -> FlatError {
29 let mut form_errors = Vec::new();
30 let mut field_errors: HashMap<String, Vec<String>> = HashMap::new();
31
32 for issue in &error.issues {
33 if issue.path.is_empty() {
34 form_errors.push(issue.message.clone());
35 } else {
36 let key = issue.path[0].to_string();
37 let key = key.strip_prefix('.').unwrap_or(&key).to_string();
39 field_errors
40 .entry(key)
41 .or_default()
42 .push(issue.message.clone());
43 }
44 }
45
46 FlatError {
47 form_errors,
48 field_errors,
49 }
50}
51
52#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
54pub struct ErrorTree {
55 pub errors: Vec<String>,
57 pub properties: HashMap<String, ErrorTree>,
59 pub items: Vec<Option<ErrorTree>>,
61}
62
63pub fn treeify_error(error: &VldError) -> ErrorTree {
65 let mut root = ErrorTree::default();
66
67 for issue in &error.issues {
68 if issue.path.is_empty() {
69 root.errors.push(issue.message.clone());
70 continue;
71 }
72
73 let mut current = &mut root;
74
75 for (i, segment) in issue.path.iter().enumerate() {
76 let is_last = i == issue.path.len() - 1;
77
78 match segment {
79 crate::error::PathSegment::Field(name) => {
80 if !current.properties.contains_key(name) {
81 current
82 .properties
83 .insert(name.clone(), ErrorTree::default());
84 }
85 if is_last {
86 current
87 .properties
88 .get_mut(name)
89 .unwrap()
90 .errors
91 .push(issue.message.clone());
92 } else {
93 current = current.properties.get_mut(name).unwrap();
94 }
95 }
96 crate::error::PathSegment::Index(idx) => {
97 while current.items.len() <= *idx {
98 current.items.push(None);
99 }
100 if current.items[*idx].is_none() {
101 current.items[*idx] = Some(ErrorTree::default());
102 }
103 if is_last {
104 current.items[*idx]
105 .as_mut()
106 .unwrap()
107 .errors
108 .push(issue.message.clone());
109 } else {
110 current = current.items[*idx].as_mut().unwrap();
111 }
112 }
113 }
114 }
115 }
116
117 root
118}
119
120pub fn prettify_error(error: &VldError) -> String {
130 let mut lines = Vec::new();
131
132 for issue in &error.issues {
133 lines.push(format!("✖ {}", issue.message));
134 if !issue.path.is_empty() || issue.received.is_some() {
135 let mut parts = Vec::new();
136 if !issue.path.is_empty() {
137 let path_str: String = issue.path.iter().map(|p| p.to_string()).collect();
138 parts.push(format!("at {}", path_str));
139 }
140 if let Some(val) = &issue.received {
141 parts.push(format!(
142 "received {}",
143 crate::error::format_value_short(val)
144 ));
145 }
146 lines.push(format!(" → {}", parts.join(", ")));
147 }
148 }
149
150 lines.join("\n")
151}