1use std::fmt;
27use regex::Regex;
28use serde_json::Value;
29use lazy_static::lazy_static;
30
31use crate::errors::Error;
32
33
34lazy_static! {
35 static ref MSG_RE: Regex = Regex::new(r#"(?x)
36 ^
37 (?P<type>[*?\w]+) # message type (verb)
38 (?: \s
39 (?P<spec>[\w:<>]+) # spec (object)
40 (?: \s
41 (?P<json>.*) # data (json)
42 )?
43 )?
44 $
45 "#).expect("valid regex");
46}
47
48pub const IDENT_REPLY: &str = "SINE2020&ISSE,SECoP,V2019-09-16,v1.0";
49
50#[derive(Debug, Clone)]
53pub enum Msg {
54 Idn,
56 IdnReply { encoded: String },
58 Describe,
60 Describing { id: String, structure: Value },
62 Activate { module: String },
64 Active { module: String },
66 Deactivate { module: String },
68 Inactive { module: String },
70 Do { module: String, command: String, arg: Value },
72 Done { module: String, command: String, data: Value },
74 Change { module: String, param: String, value: Value },
76 Changed { module: String, param: String, data: Value },
78 Read { module: String, param: String },
80 Ping { token: String },
82 Pong { token: String, data: Value },
84 ErrMsg { class: String, report: Value },
86 Update { module: String, param: String, data: Value },
88
89 InitUpdates { module: String, updates: Vec<Msg> },
91 Quit,
93}
94
95#[derive(Clone)]
98pub struct IncomingMsg(pub String, pub Msg);
99
100use self::Msg::*;
101
102mod wire {
103 pub const IDN: &str = "*IDN?";
104 pub const DESCRIBE: &str = "describe";
105 pub const DESCRIBING: &str = "describing";
106 pub const ACTIVATE: &str = "activate";
107 pub const ACTIVE: &str = "active";
108 pub const DEACTIVATE: &str = "deactivate";
109 pub const INACTIVE: &str = "inactive";
110 pub const PING: &str = "ping";
111 pub const PONG: &str = "pong";
112 pub const ERROR: &str = "error";
113 pub const DO: &str = "do";
114 pub const DONE: &str = "done";
115 pub const CHANGE: &str = "change";
116 pub const CHANGED: &str = "changed";
117 pub const READ: &str = "read";
118 pub const UPDATE: &str = "update";
119}
120
121impl Msg {
122 pub fn parse(msg: String) -> Result<IncomingMsg, Msg> {
126 match Self::parse_inner(&msg) {
127 Ok(v) => Ok(IncomingMsg(msg, v)),
128 Err(e) => Err(e.into_msg(msg)),
129 }
130 }
131
132 fn parse_inner(msg: &str) -> Result<Msg, Error> {
133 if let Some(captures) = MSG_RE.captures(msg) {
134 let action = captures.get(1).expect("is required").as_str();
135
136 let specifier = captures.get(2).map(|m| m.as_str()).unwrap_or("");
137 let mut spec_split = specifier.splitn(2, ':').map(Into::into);
138 let module = spec_split.next().expect("cannot be absent");
139 let mut param = || spec_split.next().ok_or(Error::protocol("missing parameter"));
140
141 let data = if let Some(jsonstr) = captures.get(3) {
142 serde_json::from_str(jsonstr.as_str())
143 .map_err(|_| Error::protocol("invalid JSON"))?
144 } else {
145 Value::Null
146 };
147
148 let parsed = match action {
149 wire::READ => Read { module, param: param()? },
150 wire::CHANGE => Change { module, param: param()?, value: data },
151 wire::DO => Do { module, command: param()?, arg: data },
152 wire::DESCRIBE => Describe,
153 wire::ACTIVATE => Activate { module },
154 wire::DEACTIVATE => Deactivate { module },
155 wire::PING => Ping { token: specifier.into() },
156 wire::IDN => Idn,
157 wire::UPDATE => Update { module, param: param()?, data },
158 wire::CHANGED => Changed { module, param: param()?, data },
159 wire::DONE => Done { module, command: param()?, data },
160 wire::DESCRIBING => Describing { id: specifier.into(), structure: data },
161 wire::ACTIVE => Active { module },
162 wire::INACTIVE => Inactive { module },
163 wire::PONG => Pong { token: specifier.into(), data },
164 wire::ERROR => ErrMsg { class: specifier.into(), report: data },
165 _ => return Err(Error::protocol("no such message type"))
166 };
167
168 Ok(parsed)
169 } else if msg == IDENT_REPLY {
170 Ok(IdnReply { encoded: IDENT_REPLY.into() })
172 } else {
173 Err(Error::protocol("invalid message format"))
174 }
175 }
176}
177
178impl fmt::Display for Msg {
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184 match self {
185 Update { module, param, data } =>
186 write!(f, "{} {}:{} {}", wire::UPDATE, module, param, data),
187 Changed { module, param, data } =>
188 write!(f, "{} {}:{} {}", wire::CHANGED, module, param, data),
189 Done { module, command, data } =>
190 write!(f, "{} {}:{} {}", wire::DONE, module, command, data),
191 Describing { id, structure } =>
192 write!(f, "{} {} {}", wire::DESCRIBING, id, structure),
193 Active { module } =>
194 if module.is_empty() { f.write_str(wire::ACTIVE) }
195 else { write!(f, "{} {}", wire::ACTIVE, module) },
196 Inactive { module } =>
197 if module.is_empty() { f.write_str(wire::INACTIVE) }
198 else { write!(f, "{} {}", wire::INACTIVE, module) },
199 Pong { token, data } =>
200 write!(f, "{} {} {}", wire::PONG, token, data),
201 Idn => f.write_str(wire::IDN),
202 IdnReply { encoded } => f.write_str(&encoded),
203 Read { module, param } =>
204 write!(f, "{} {}:{}", wire::READ, module, param),
205 Change { module, param, value } =>
206 write!(f, "{} {}:{} {}", wire::CHANGE, module, param, value),
207 Do { module, command, arg } =>
208 write!(f, "{} {}:{} {}", wire::DO, module, command, arg),
209 Describe => f.write_str(wire::DESCRIBE),
210 Activate { module } =>
211 if module.is_empty() { f.write_str(wire::ACTIVATE) }
212 else { write!(f, "{} {}", wire::ACTIVATE, module) },
213 Deactivate { module } =>
214 if module.is_empty() { f.write_str(wire::DEACTIVATE) }
215 else { write!(f, "{} {}", wire::DEACTIVATE, module) },
216 Ping { token } =>
217 if token.is_empty() { f.write_str(wire::PING) }
218 else { write!(f, "{} {}", wire::PING, token) },
219 ErrMsg { class, report } =>
220 write!(f, "{} {} {}", wire::ERROR, class, report),
221 InitUpdates { .. } => write!(f, "<updates>"),
222 Quit => write!(f, "<eof>"),
223 }
224 }
225}
226
227impl IncomingMsg {
228 pub fn bare(msg: Msg) -> Self {
229 IncomingMsg(String::new(), msg)
230 }
231}
232
233impl fmt::Display for IncomingMsg {
234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235 write!(f, "{}", self.0)
236 }
237}