dictproto/
connection.rs

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    // Read / Write things
16    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    // Generic
38    ReplyOnly,
39    OkReply,
40
41    // TODO: parse capabilities too
42    InitialConnection(String),
43
44    // DEFINE packets
45    DefinitionsFollow,
46    Definition(Definition),
47
48    // MATCH packets
49    Matches(Vec<Match>),
50
51    // SHOW packets
52    Databases(Vec<Database>),
53    Strategies(Vec<Strategy>), // TODO: There is way more specific packets
54}
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        // start of answer
120        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            // Starting a string
232            if let Some(suffix) = part.strip_prefix('"') {
233                if let Some(oneword) = suffix.strip_suffix('"') {
234                    // That ends here too
235                    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            // Generic
274            Status(ReplyKind::PositiveCompletion, Category::System, 0) => {
275                Some(Ok(DICTPacket(DICTPacketKind::OkReply, reply)))
276            }
277
278            // Connection open
279            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            // DEFINE Command
294            Status(ReplyKind::PositivePreliminary, Category::System, 0) => {
295                Some(Ok(DICTPacket(DICTPacketKind::DefinitionsFollow, reply)))
296            }
297            Status(ReplyKind::PositivePreliminary, Category::System, 1) => {
298                // Definition
299
300                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            // MATCH command
326            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            // SHOW DB command
351            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            // SHOW STRAT command
376            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}