Skip to main content

link_cli/protocol/
client.rs

1//! A TCP client for [`LinksServer`](super::LinksServer).
2
3use super::error::{ProtocolError, ProtocolResult};
4use super::format::{format_document, parse_document};
5use super::mapping::LinoDocument;
6use super::protocols::{LinoConnection, LinoProtocol};
7use super::server::error_message;
8use std::net::{TcpStream, ToSocketAddrs};
9
10/// Sends substitution queries to a LiNo server over any [`LinoProtocol`].
11#[derive(Debug)]
12pub struct LinksClient {
13    connection: LinoConnection<TcpStream, Box<dyn LinoProtocol>>,
14}
15
16impl LinksClient {
17    /// Connects to `address` using `protocol` for every message.
18    pub fn connect(
19        address: impl ToSocketAddrs,
20        protocol: impl LinoProtocol + 'static,
21    ) -> ProtocolResult<Self> {
22        let stream = TcpStream::connect(address)?;
23        let _ = stream.set_nodelay(true);
24        Ok(Self {
25            connection: LinoConnection::new(stream, Box::new(protocol)),
26        })
27    }
28
29    /// Sends a parsed document and returns the reply document.
30    ///
31    /// An `(error: 'message')` reply becomes [`ProtocolError::Remote`].
32    pub fn request(&mut self, document: &LinoDocument) -> ProtocolResult<LinoDocument> {
33        let reply = self.connection.request(document)?;
34        if let Some(message) = error_message(&reply) {
35            return Err(ProtocolError::Remote(message.to_string()));
36        }
37        Ok(reply)
38    }
39
40    /// Runs a LiNo substitution query; the empty query reads every link.
41    pub fn query(&mut self, query: &str) -> ProtocolResult<LinoDocument> {
42        self.request(&parse_document(query)?)
43    }
44
45    /// Like [`query`](Self::query), returning the reply as canonical text.
46    pub fn query_text(&mut self, query: &str) -> ProtocolResult<String> {
47        Ok(format_document(&self.query(query)?))
48    }
49}