Skip to main content

zlink_core/idl/
method.rs

1//! Method definitions for Varlink IDL.
2
3use core::fmt;
4
5use alloc::vec::Vec;
6
7use super::{Comment, List, Parameter};
8
9/// A method definition in Varlink IDL.
10#[derive(Debug, Clone, Eq)]
11pub struct Method<'a> {
12    /// The name of the method.
13    name: &'a str,
14    /// Input parameters for the method.
15    inputs: List<'a, Parameter<'a>>,
16    /// Output parameters for the method.
17    outputs: List<'a, Parameter<'a>>,
18    /// Comments associated with this method.
19    comments: List<'a, Comment<'a>>,
20}
21
22impl<'a> Method<'a> {
23    /// Creates a new method with the given name, borrowed parameters, and comments.
24    pub const fn new(
25        name: &'a str,
26        inputs: &'a [&'a Parameter<'a>],
27        outputs: &'a [&'a Parameter<'a>],
28        comments: &'a [&'a Comment<'a>],
29    ) -> Self {
30        Self {
31            name,
32            inputs: List::Borrowed(inputs),
33            outputs: List::Borrowed(outputs),
34            comments: List::Borrowed(comments),
35        }
36    }
37
38    /// Creates a new method with the given name, owned parameters, and comments.
39    pub fn new_owned(
40        name: &'a str,
41        inputs: Vec<Parameter<'a>>,
42        outputs: Vec<Parameter<'a>>,
43        comments: Vec<Comment<'a>>,
44    ) -> Self {
45        Self {
46            name,
47            inputs: List::from(inputs),
48            outputs: List::from(outputs),
49            comments: List::from(comments),
50        }
51    }
52
53    /// Returns the name of the method.
54    pub fn name(&self) -> &'a str {
55        self.name
56    }
57
58    /// Returns an iterator over the input parameters.
59    pub fn inputs(&self) -> impl Iterator<Item = &Parameter<'a>> {
60        self.inputs.iter()
61    }
62
63    /// Returns an iterator over the output parameters.
64    pub fn outputs(&self) -> impl Iterator<Item = &Parameter<'a>> {
65        self.outputs.iter()
66    }
67
68    /// Returns true if the method has no input parameters.
69    pub fn has_no_inputs(&self) -> bool {
70        self.inputs.is_empty()
71    }
72
73    /// Returns true if the method has no output parameters.
74    pub fn has_no_outputs(&self) -> bool {
75        self.outputs.is_empty()
76    }
77
78    /// Returns the comments associated with this method.
79    pub fn comments(&self) -> impl Iterator<Item = &Comment<'a>> {
80        self.comments.iter()
81    }
82}
83
84impl<'a> fmt::Display for Method<'a> {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        // Comments first
87        for comment in self.comments.iter() {
88            writeln!(f, "{comment}")?;
89        }
90        write!(f, "method {}(", self.name)?;
91        let mut first = true;
92        for param in self.inputs.iter() {
93            if !first {
94                write!(f, ", ")?;
95            }
96            first = false;
97            write!(f, "{param}")?;
98        }
99        write!(f, ")")?;
100
101        write!(f, " -> (")?;
102        let mut first = true;
103        for param in self.outputs.iter() {
104            if !first {
105                write!(f, ", ")?;
106            }
107            first = false;
108            write!(f, "{param}")?;
109        }
110        write!(f, ")")?;
111
112        Ok(())
113    }
114}
115
116impl PartialEq for Method<'_> {
117    fn eq(&self, other: &Self) -> bool {
118        self.name == other.name && self.inputs == other.inputs && self.outputs == other.outputs
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use crate::idl::{Parameter, Type};
126
127    #[test]
128    fn method_creation() {
129        let input = Parameter::new("id", &Type::Int, &[]);
130        let output = Parameter::new("name", &Type::String, &[]);
131        let inputs = [&input];
132        let outputs = [&output];
133
134        let method = Method::new("GetName", &inputs, &outputs, &[]);
135        assert_eq!(method.name(), "GetName");
136        assert_eq!(method.inputs().count(), 1);
137        assert_eq!(method.outputs().count(), 1);
138        assert!(!method.has_no_inputs());
139        assert!(!method.has_no_outputs());
140
141        // Check the parameters individually - order and values.
142        let inputs_vec: Vec<_> = method.inputs().collect();
143        assert_eq!(inputs_vec[0].name(), "id");
144        assert_eq!(inputs_vec[0].ty(), &Type::Int);
145
146        let outputs_vec: Vec<_> = method.outputs().collect();
147        assert_eq!(outputs_vec[0].name(), "name");
148        assert_eq!(outputs_vec[0].ty(), &Type::String);
149    }
150
151    #[test]
152    fn method_no_params() {
153        let method = Method::new("Ping", &[], &[], &[]);
154        assert_eq!(method.name(), "Ping");
155        assert!(method.has_no_inputs());
156        assert!(method.has_no_outputs());
157    }
158
159    #[test]
160    fn method_display_with_no_outputs() {
161        use core::fmt::Write;
162
163        let method = Method::new("Ping", &[], &[], &[]);
164        let mut displayed = String::new();
165        write!(&mut displayed, "{}", method).unwrap();
166        assert_eq!(displayed, "method Ping() -> ()");
167
168        let name_param = Parameter::new("name", &Type::String, &[]);
169        let id_param = Parameter::new("id", &Type::String, &[]);
170        let params = [&name_param, &id_param];
171        let method = Method::new("Register", &params, &[], &[]);
172        let mut displayed = String::new();
173        write!(&mut displayed, "{}", method).unwrap();
174        assert_eq!(displayed, "method Register(name: string, id: string) -> ()");
175    }
176
177    #[test]
178    fn display_with_comments() {
179        use crate::idl::Comment;
180        use core::fmt::Write;
181
182        let comment1 = Comment::new("Get user information");
183        let comment2 = Comment::new("Returns user details by ID");
184        let comments = [&comment1, &comment2];
185
186        let input = Parameter::new("id", &Type::Int, &[]);
187        let output = Parameter::new("user", &Type::Custom("User"), &[]);
188        let inputs = [&input];
189        let outputs = [&output];
190
191        let method = Method::new("GetUser", &inputs, &outputs, &comments);
192        let mut displayed = String::new();
193        write!(&mut displayed, "{}", method).unwrap();
194        assert_eq!(displayed, "# Get user information\n# Returns user details by ID\nmethod GetUser(id: int) -> (user: User)");
195    }
196}