1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct ValidationIssue {
15 pub node_id: Option<String>,
17 pub message: String,
19}
20
21impl std::fmt::Display for ValidationIssue {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match &self.node_id {
24 Some(id) => write!(f, "node '{}': {}", id, self.message),
25 None => write!(f, "{}", self.message),
26 }
27 }
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33
34 #[test]
35 fn display_with_node_id() {
36 let issue = ValidationIssue {
37 node_id: Some("fetch".into()),
38 message: "connection refused".into(),
39 };
40 assert_eq!(format!("{}", issue), "node 'fetch': connection refused");
41 }
42
43 #[test]
44 fn display_without_node_id() {
45 let issue = ValidationIssue {
46 node_id: None,
47 message: "missing start node".into(),
48 };
49 assert_eq!(format!("{}", issue), "missing start node");
50 }
51
52 #[test]
53 fn debug_round_trip() {
54 let issue = ValidationIssue {
55 node_id: Some("a".into()),
56 message: "b".into(),
57 };
58 let debug = format!("{:?}", issue);
59 assert!(debug.contains("ValidationIssue"));
60 assert!(debug.contains("a"));
61 assert!(debug.contains("b"));
62 }
63
64 #[test]
65 fn serde_round_trip() {
66 let issue = ValidationIssue {
67 node_id: Some("n".into()),
68 message: "m".into(),
69 };
70 let json = serde_json::to_string(&issue).unwrap();
71 let back: ValidationIssue = serde_json::from_str(&json).unwrap();
72 assert_eq!(back.node_id, issue.node_id);
73 assert_eq!(back.message, issue.message);
74 }
75
76 #[test]
77 fn clone_is_equal() {
78 let issue = ValidationIssue {
79 node_id: Some("x".into()),
80 message: "y".into(),
81 };
82 let cloned = issue.clone();
83 assert_eq!(cloned, issue);
84 }
85}