1use super::{Database, Definition, Match, Strategy};
2use crate::reply::{ParseReplyError, Reply};
3use crate::status::{Category, ReplyKind, Status};
4use std::convert::From;
5use std::io::{BufRead, BufReader, BufWriter, Write};
6use std::net::TcpStream;
7use std::ops::Drop;
8
9#[derive(Debug)]
10pub enum DICTError {
11 ReplyError(ParseReplyError),
12 SystemError(Reply),
13 UnexpectedPacket(DICTPacket),
14
15 NoAnswer,
17 ReadWriteError(std::io::Error),
18 MalformedAnswer(&'static str),
19}
20
21impl From<ParseReplyError> for DICTError {
22 fn from(src: ParseReplyError) -> Self {
23 DICTError::ReplyError(src)
24 }
25}
26
27impl From<std::io::Error> for DICTError {
28 fn from(src: std::io::Error) -> Self {
29 DICTError::ReadWriteError(src)
30 }
31}
32
33pub type DICTResult<T> = Result<(T, Reply), DICTError>;
34
35#[derive(Debug)]
36pub enum DICTPacketKind {
37 ReplyOnly,
39 OkReply,
40
41 InitialConnection(String),
43
44 DefinitionsFollow,
46 Definition(Definition),
47
48 Matches(Vec<Match>),
50
51 Databases(Vec<Database>),
53 Strategies(Vec<Strategy>), }
55
56#[derive(Debug)]
57pub struct DICTPacket(pub DICTPacketKind, pub Reply);
58
59pub struct DICTConnection {
60 input: BufReader<TcpStream>,
61 output: BufWriter<TcpStream>,
62}
63
64impl DICTConnection {
65 pub fn new(inner: TcpStream) -> std::io::Result<Self> {
66 let input = BufReader::new(inner.try_clone()?);
67 Ok(DICTConnection {
68 input,
69 output: BufWriter::new(inner),
70 })
71 }
72
73 fn read_raw_text(&mut self) -> Vec<String> {
74 let mut line = String::new();
75 let mut text = Vec::with_capacity(10);
76
77 loop {
78 if self.input.read_line(&mut line).is_ok() {
79 let line_t = line.trim_end_matches("\r\n");
80 if line_t.eq(".") {
81 break;
82 } else {
83 text.push(line_t.to_owned());
84 line.clear();
85 }
86 }
87 }
88
89 text
90 }
91
92 pub fn start(&mut self) -> DICTResult<String> {
93 match self.next().ok_or(DICTError::NoAnswer)?? {
94 DICTPacket(DICTPacketKind::InitialConnection(msg_id), r) => Ok((msg_id, r)),
95 e => Err(DICTError::UnexpectedPacket(e)),
96 }
97 }
98
99 pub fn client(&mut self, client: String) -> Result<Reply, DICTError> {
100 writeln!(self.output, "CLIENT \"{}\"", client)?;
101 self.output.flush()?;
102
103 match self.next().ok_or(DICTError::NoAnswer)?? {
104 DICTPacket(DICTPacketKind::OkReply, r) => Ok(r),
105 e => Err(DICTError::UnexpectedPacket(e)),
106 }
107 }
108
109 pub fn define(
110 &mut self,
111 database: Database,
112 word: String,
113 ) -> Result<(Vec<Definition>, Reply), DICTError> {
114 writeln!(self.output, "DEFINE \"{}\" \"{}\"", database.name, word)?;
115 self.output.flush()?;
116
117 let reply = self.next().ok_or(DICTError::NoAnswer)??;
118
119 match reply {
121 DICTPacket(DICTPacketKind::DefinitionsFollow, _) => {}
122 p => {
123 return Err(DICTError::UnexpectedPacket(p));
124 }
125 }
126
127 let mut defs: Vec<Definition> = Vec::new();
128
129 for p in self {
130 match p {
131 Ok(DICTPacket(DICTPacketKind::Definition(def), _)) => {
132 defs.push(def);
133 }
134 Ok(DICTPacket(DICTPacketKind::OkReply, _)) => {
135 break;
136 }
137 Ok(unexp) => {
138 return Err(DICTError::UnexpectedPacket(unexp));
139 }
140 Err(e) => {
141 return Err(e);
142 }
143 }
144 }
145
146 Ok((defs, reply.1))
147 }
148
149 pub fn match_db(
150 &mut self,
151 db: Database,
152 strat: Strategy,
153 word: String,
154 ) -> Result<(Vec<Match>, Reply), DICTError> {
155 writeln!(
156 self.output,
157 "MATCH \"{}\" \"{}\" \"{}\"",
158 db.name, strat.name, word
159 )?;
160 self.output.flush()?;
161
162 match self.next().ok_or(DICTError::NoAnswer)?? {
163 DICTPacket(DICTPacketKind::Matches(matches), r) => {
164 let ok = self.next().ok_or(DICTError::NoAnswer)??;
165
166 if let DICTPacket(DICTPacketKind::OkReply, _) = ok {
167 Ok((matches, r))
168 } else {
169 Err(DICTError::UnexpectedPacket(ok))
170 }
171 }
172 e => Err(DICTError::UnexpectedPacket(e)),
173 }
174 }
175
176 pub fn show_db(&mut self) -> Result<(Vec<Database>, Reply), DICTError> {
177 writeln!(self.output, "SHOW DATABASES")?;
178 self.output.flush()?;
179
180 match self.next().ok_or(DICTError::NoAnswer)?? {
181 DICTPacket(DICTPacketKind::Databases(dbs), r) => {
182 let ok = self.next().ok_or(DICTError::NoAnswer)??;
183
184 if let DICTPacket(DICTPacketKind::OkReply, _) = ok {
185 Ok((dbs, r))
186 } else {
187 Err(DICTError::UnexpectedPacket(ok))
188 }
189 }
190 e => Err(DICTError::UnexpectedPacket(e)),
191 }
192 }
193
194 pub fn show_strat(&mut self) -> Result<(Vec<Strategy>, Reply), DICTError> {
195 writeln!(self.output, "SHOW STRATEGIES")?;
196 self.output.flush()?;
197
198 match self.next().ok_or(DICTError::NoAnswer)?? {
199 DICTPacket(DICTPacketKind::Strategies(strats), r) => {
200 let ok = self.next().ok_or(DICTError::NoAnswer)??;
201
202 if let DICTPacket(DICTPacketKind::OkReply, _) = ok {
203 Ok((strats, r))
204 } else {
205 Err(DICTError::UnexpectedPacket(ok))
206 }
207 }
208 e => Err(DICTError::UnexpectedPacket(e)),
209 }
210 }
211}
212
213macro_rules! get_argument {
214 ($arguments:ident, $index:expr, $err:expr) => {
215 if let Some(arg) = $arguments.get($index) {
216 arg
217 } else {
218 return Some(Err($err));
219 }
220 };
221}
222
223fn parse_cmd_argument(reply_text: &String) -> Vec<String> {
224 let mut ret: Vec<String> = Vec::new();
225 let mut tmp: String = String::new();
226
227 let mut in_string: bool = false;
228
229 for part in reply_text.split_ascii_whitespace() {
230 if !in_string {
231 if let Some(suffix) = part.strip_prefix('"') {
233 if let Some(oneword) = suffix.strip_suffix('"') {
234 ret.push(String::from(oneword));
236 } else {
237 in_string = true;
238
239 tmp.push_str(suffix);
240 }
241 } else {
242 ret.push(String::from(part));
243 }
244 } else {
245 tmp.push_str(" ");
246 if let Some(preffix) = part.strip_suffix('"') {
247 tmp.push_str(preffix);
248 ret.push(tmp);
249
250 in_string = false;
251 tmp = String::new();
252 } else {
253 tmp.push_str(part);
254 }
255 }
256 }
257
258 ret
259}
260
261impl Iterator for DICTConnection {
262 type Item = Result<DICTPacket, DICTError>;
263
264 fn next(&mut self) -> Option<Self::Item> {
265 let reply = match Reply::from_reader(&mut self.input) {
266 Ok(rep) => rep,
267 Err(e) => {
268 return Some(Err(DICTError::ReplyError(e)));
269 }
270 };
271
272 match reply.status {
273 Status(ReplyKind::PositiveCompletion, Category::System, 0) => {
275 Some(Ok(DICTPacket(DICTPacketKind::OkReply, reply)))
276 }
277
278 Status(ReplyKind::PositiveCompletion, Category::Connection, 0) => {
280 let arguments = reply.text.split_whitespace().collect::<Vec<&str>>();
281
282 let msg_id = get_argument!(
283 arguments,
284 arguments.len() - 1,
285 DICTError::MalformedAnswer("Missing starting text")
286 );
287 Some(Ok(DICTPacket(
288 DICTPacketKind::InitialConnection((*msg_id).to_owned()),
289 reply,
290 )))
291 }
292
293 Status(ReplyKind::PositivePreliminary, Category::System, 0) => {
295 Some(Ok(DICTPacket(DICTPacketKind::DefinitionsFollow, reply)))
296 }
297 Status(ReplyKind::PositivePreliminary, Category::System, 1) => {
298 let arguments = parse_cmd_argument(&reply.text);
301 let dbname = get_argument!(
302 arguments,
303 1,
304 DICTError::MalformedAnswer("Missing database name")
305 );
306 let dbdesc = get_argument!(
307 arguments,
308 2,
309 DICTError::MalformedAnswer("Missing database description")
310 );
311
312 let text = self.read_raw_text();
313
314 let def = Definition {
315 source: Database {
316 name: String::from(dbname),
317 desc: String::from(dbdesc),
318 },
319 text,
320 };
321
322 Some(Ok(DICTPacket(DICTPacketKind::Definition(def), reply)))
323 }
324
325 Status(ReplyKind::PositivePreliminary, Category::System, 2) => {
327 let mut matches: Vec<Match> = Vec::new();
328 for match_def in self.read_raw_text() {
329 let arguments = parse_cmd_argument(&match_def);
330 let dbname = get_argument!(
331 arguments,
332 0,
333 DICTError::MalformedAnswer("Missing database name")
334 );
335 let word = get_argument!(
336 arguments,
337 1,
338 DICTError::MalformedAnswer("Missing database description")
339 );
340
341 matches.push(Match {
342 source: Database::from(dbname.to_owned()),
343 word: word.to_owned(),
344 });
345 }
346
347 Some(Ok(DICTPacket(DICTPacketKind::Matches(matches), reply)))
348 }
349
350 Status(ReplyKind::PositivePreliminary, Category::Information, 0) => {
352 let mut dbs: Vec<Database> = Vec::new();
353 for db_def in self.read_raw_text() {
354 let arguments = parse_cmd_argument(&db_def);
355 let name = get_argument!(
356 arguments,
357 0,
358 DICTError::MalformedAnswer("Missing database name")
359 );
360 let desc = get_argument!(
361 arguments,
362 1,
363 DICTError::MalformedAnswer("Missing database description")
364 );
365
366 dbs.push(Database {
367 name: name.to_owned(),
368 desc: desc.to_owned(),
369 });
370 }
371
372 Some(Ok(DICTPacket(DICTPacketKind::Databases(dbs), reply)))
373 }
374
375 Status(ReplyKind::PositivePreliminary, Category::Information, 1) => {
377 let mut strats: Vec<Strategy> = Vec::new();
378 for strat_def in self.read_raw_text() {
379 let arguments = parse_cmd_argument(&strat_def);
380 let name = get_argument!(
381 arguments,
382 0,
383 DICTError::MalformedAnswer("Missing database name")
384 );
385 let desc = get_argument!(
386 arguments,
387 1,
388 DICTError::MalformedAnswer("Missing database description")
389 );
390
391 strats.push(Strategy {
392 name: name.to_owned(),
393 desc: desc.to_owned(),
394 });
395 }
396
397 Some(Ok(DICTPacket(DICTPacketKind::Strategies(strats), reply)))
398 }
399 ref r if r.is_positive() => Some(Ok(DICTPacket(DICTPacketKind::ReplyOnly, reply))),
400 _ => Some(Err(DICTError::SystemError(reply))),
401 }
402 }
403}
404
405impl Drop for DICTConnection {
406 fn drop(&mut self) {
407 writeln!(self.output, "QUIT").ok();
408 }
409}