1use regex::Regex;
2use thiserror::Error;
3
4pub mod enumerations;
5pub mod primitives;
6
7#[derive(Error, Debug)]
8pub enum Error {
9 #[error("Received data does not match expected format: {0}")]
12 ResponseDecoding(String),
13}
14
15pub type Result<T> = std::result::Result<T, Error>;
16
17pub trait ScpiSerialize {
18 fn serialize(&self, out: &mut String);
19
20 fn serialize_to_string(&self) -> String {
21 let mut out = String::new();
22 self.serialize(&mut out);
23 out
24 }
25}
26
27pub trait ScpiDeserialize
28where
29 Self: Sized,
30{
31 fn deserialize(input: &mut &str) -> Result<Self>;
35
36 fn deserialize_complete(mut input: &str) -> Result<Self> {
37 let result = Self::deserialize(&mut input)?;
38 check_empty(input).unwrap();
39 Ok(result)
40 }
41}
42
43pub trait ScpiRequest: ScpiSerialize {
44 type Response;
48}
49
50impl<T: ScpiSerialize> ScpiSerialize for Option<T> {
52 fn serialize(&self, out: &mut String) {
53 if let Some(inner) = self {
54 inner.serialize(out);
55 }
56 }
57}
58
59pub struct EmptyResponse;
63
64#[macro_export]
65macro_rules! impl_scpi_serialize {
66 ($type:ty, [ $( $part:tt $(as $converter:ty)? ),* $(,)? ]) => {
67 impl $crate::ScpiSerialize for $type {
68 fn serialize(&self, out: &mut String) {
69 $(
70 impl_scpi_serialize!(@part self, out, $part $(as $converter)*);
71 )*
72 }
73 }
74 };
75
76 (@part $self:ident, $out:ident, $lit:literal) => {
78 $out.push_str($lit);
79 };
80
81 (@part $self:ident, $out:ident, $field:ident) => {
83 $self.$field.serialize($out);
84 };
85
86 (@part $self:ident, $out:ident, $field:ident as $converter:ty) => {
87 let convert : $converter = $self.$field.into();
88 convert.serialize($out);
89 };
90}
91
92#[macro_export]
94macro_rules! impl_scpi_request {
95 ($request:ty, $response:ty) => {
96 impl $crate::ScpiRequest for $request {
97 type Response = $response;
98 }
99 };
100}
101
102pub fn match_literal(input: &mut &str, literal: &'static str) -> Result<()> {
103 if let Some(rest) = input.strip_prefix(literal) {
104 *input = rest;
105 Ok(())
106 } else {
107 Err(Error::ResponseDecoding(format!(
108 "Expected literal `{literal}` not matched `{input}`"
109 )))
110 }
111}
112
113pub fn read_until<'a>(input: &mut &'a str, delimiter: char) -> Result<&'a str> {
114 if let Some(index) = input.find(delimiter) {
115 let (head, tail) = input.split_at(index);
116 *input = &tail[1..]; Ok(head)
118 } else {
119 Err(Error::ResponseDecoding(format!(
120 "Expected `{delimiter}` in `{input}`"
121 )))
122 }
123}
124
125pub fn read_prefix<'a>(input: &mut &'a str, pattern: &Regex) -> &'a str {
126 let length = pattern.find(input).map_or(0, |m| m.end());
127 let (head, tail) = input.split_at(length);
128 *input = tail;
129 head
130}
131
132pub fn read_exact<'a>(input: &mut &'a str, len: usize) -> Result<&'a str> {
133 if input.len() < len {
134 return Err(Error::ResponseDecoding(format!(
135 "Failed to read {len} characters from `{input}`"
136 )));
137 }
138
139 let (head, tail) = input.split_at(len);
140 *input = tail;
141 Ok(head)
142}
143
144pub fn read_all(input: &mut &str) -> Result<String> {
145 let result = input.to_string();
146 *input = "";
147 Ok(result)
148}
149
150pub fn check_empty(input: &str) -> Result<()> {
151 if input.is_empty() {
152 Ok(())
153 } else {
154 Err(Error::ResponseDecoding(format!(
155 "Response should be empty/fully deserialized, but still has content: `{input}`"
156 )))
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_check_empty() {
166 assert!(check_empty("").is_ok());
167 assert!(check_empty("x").is_err());
168 }
169
170 #[test]
171 fn test_read_exact() {
172 let input = &mut "1234";
173 assert_eq!(read_exact(input, 2).unwrap(), "12");
174 assert!(read_exact(input, 3).is_err());
175 assert_eq!(read_exact(input, 2).unwrap(), "34");
176 assert!(check_empty(input).is_ok());
177 }
178
179 #[test]
180 fn test_match_literal() {
181 let input = &mut "1234";
182 assert!(match_literal(input, "12").is_ok());
183 assert!(match_literal(input, "12").is_err());
184 assert!(match_literal(input, "34").is_ok());
185 assert!(check_empty(input).is_ok());
186 }
187
188 #[test]
189 fn test_read_until() {
190 let input = &mut "12,34";
191 assert_eq!(read_until(input, ',').unwrap(), "12");
192 assert!(match_literal(input, "34").is_ok());
193 assert!(check_empty(input).is_ok());
194 }
195
196 #[test]
197 fn test_read_prefix() {
198 let input = &mut "12,34";
199 let pattern = regex::Regex::new("^[0-9]+").unwrap();
200 assert_eq!(read_prefix(input, &pattern), "12");
201 assert!(match_literal(input, ",").is_ok());
202 assert_eq!(read_prefix(input, &pattern), "34");
203 assert!(check_empty(input).is_ok());
204 }
205
206 #[test]
207 fn test_read_all() {
208 let input = &mut "12,34\nasdf";
209 assert_eq!(read_all(input).unwrap(), "12,34\nasdf");
210 }
211}