Skip to main content

zlink_core/idl/
error.rs

1//! Error definitions for Varlink IDL.
2
3use core::fmt;
4
5use alloc::vec::Vec;
6
7use super::{Comment, Field, List};
8
9/// An error definition in Varlink IDL.
10#[derive(Debug, Clone, Eq)]
11pub struct Error<'a> {
12    /// The name of the error.
13    name: &'a str,
14    /// The fields of the error.
15    fields: List<'a, Field<'a>>,
16    /// Comments associated with this error.
17    comments: List<'a, Comment<'a>>,
18}
19
20impl<'a> Error<'a> {
21    /// Creates a new error with the given name, borrowed fields, and comments.
22    pub const fn new(
23        name: &'a str,
24        fields: &'a [&'a Field<'a>],
25        comments: &'a [&'a Comment<'a>],
26    ) -> Self {
27        Self {
28            name,
29            fields: List::Borrowed(fields),
30            comments: List::Borrowed(comments),
31        }
32    }
33
34    /// Creates a new error with the given name, owned fields, and comments.
35    /// Same as `new` but takes `fields` by value.
36    pub fn new_owned(name: &'a str, fields: Vec<Field<'a>>, comments: Vec<Comment<'a>>) -> Self {
37        Self {
38            name,
39            fields: List::from(fields),
40            comments: List::from(comments),
41        }
42    }
43
44    /// Returns the name of the error.
45    pub fn name(&self) -> &'a str {
46        self.name
47    }
48
49    /// Returns an iterator over the fields of the error.
50    pub fn fields(&self) -> impl Iterator<Item = &Field<'a>> {
51        self.fields.iter()
52    }
53
54    /// Returns true if the error has no fields.
55    pub fn has_no_fields(&self) -> bool {
56        self.fields.is_empty()
57    }
58
59    /// Returns the comments associated with this error.
60    pub fn comments(&self) -> impl Iterator<Item = &Comment<'a>> {
61        self.comments.iter()
62    }
63}
64
65impl<'a> fmt::Display for Error<'a> {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        // Comments first
68        for comment in self.comments.iter() {
69            writeln!(f, "{comment}")?;
70        }
71        write!(f, "error {} (", self.name)?;
72        let mut first = true;
73        for field in self.fields.iter() {
74            if !first {
75                write!(f, ", ")?;
76            }
77            first = false;
78            write!(f, "{field}")?;
79        }
80        write!(f, ")")
81    }
82}
83
84impl<'a> PartialEq for Error<'a> {
85    fn eq(&self, other: &Self) -> bool {
86        self.name == other.name && self.fields == other.fields
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::idl::Type;
94
95    #[test]
96    fn error_creation() {
97        let message_field = Field::new("message", &Type::String, &[]);
98        let code_field = Field::new("code", &Type::Int, &[]);
99        let fields = [&message_field, &code_field];
100
101        let error = Error::new("InvalidInput", &fields, &[]);
102        assert_eq!(error.name(), "InvalidInput");
103        assert_eq!(error.fields().count(), 2);
104        assert!(!error.has_no_fields());
105
106        // Check the fields individually - order and values.
107        let fields_vec: Vec<_> = error.fields().collect();
108        assert_eq!(fields_vec[0].name(), "message");
109        assert_eq!(fields_vec[0].ty(), &Type::String);
110        assert_eq!(fields_vec[1].name(), "code");
111        assert_eq!(fields_vec[1].ty(), &Type::Int);
112    }
113
114    #[test]
115    fn error_no_fields() {
116        let error = Error::new("UnknownError", &[], &[]);
117        assert_eq!(error.name(), "UnknownError");
118        assert!(error.has_no_fields());
119    }
120
121    #[test]
122    fn display_with_comments() {
123        use crate::idl::Comment;
124        use core::fmt::Write;
125
126        let comment1 = Comment::new("Authentication failed");
127        let comment2 = Comment::new("Invalid credentials provided");
128        let comments = [&comment1, &comment2];
129
130        let message_field = Field::new("message", &Type::String, &[]);
131        let code_field = Field::new("code", &Type::Int, &[]);
132        let fields = [&message_field, &code_field];
133
134        let error = Error::new("AuthError", &fields, &comments);
135        let mut displayed = String::new();
136        write!(&mut displayed, "{}", error).unwrap();
137        assert_eq!(
138            displayed,
139            "# Authentication failed\n# Invalid credentials provided\nerror AuthError (message: string, code: int)"
140        );
141    }
142}