unicom_serial/
tokio_impl.rs

1use std::sync::Arc;
2
3#[cfg(feature = "tokio")]
4use tokio_serial::{DataBits, FlowControl, Parity, SerialPortBuilder, SerialStream, StopBits};
5
6use unicom::{Backend, BoxedConnect, BoxedConnection, BoxedConnector, Connector, Error, Url};
7
8/// Serial port backend
9///
10/// Support connecting to devices via serial ports
11#[derive(Clone)]
12pub struct SerialPort {
13    name: String,
14    description: String,
15}
16
17impl Default for SerialPort {
18    fn default() -> Self {
19        Self {
20            name: "serial-port".into(),
21            description: "Support for serial port connections.".into(),
22        }
23    }
24}
25
26impl Backend for SerialPort {
27    fn name(&self) -> &str {
28        &self.name
29    }
30
31    fn description(&self) -> &str {
32        &self.description
33    }
34
35    fn connector(&self, url: &Url) -> Option<BoxedConnector> {
36        if (url.scheme() == "serial"
37            || url.scheme() == "console"
38            || url.scheme() == "tty"
39            || url.scheme() == "com")
40            && !url.has_host()
41            && url.path() != "/"
42        {
43            let builder = tokio_serial::new(url.path(), 115200);
44            let builder =
45                if let Some(baud_rate) = url.fragment().and_then(|val| val.parse::<u32>().ok()) {
46                    builder.baud_rate(baud_rate)
47                } else {
48                    builder
49                };
50            let builder = url
51                .query_pairs()
52                .fold(builder, |builder, (name, val)| match &*name {
53                    "baud" | "baudrate" | "baud_rate" => {
54                        if let Ok(baud_rate) = val.parse::<u32>() {
55                            builder.baud_rate(baud_rate)
56                        } else {
57                            builder
58                        }
59                    }
60                    "data" | "databits" | "data_bits" => match &*val {
61                        "5" | "five" => builder.data_bits(DataBits::Five),
62                        "6" | "six" => builder.data_bits(DataBits::Six),
63                        "7" | "seven" => builder.data_bits(DataBits::Seven),
64                        "8" | "eight" => builder.data_bits(DataBits::Eight),
65                        _ => builder,
66                    },
67                    "flow" | "flowctrl" | "flowcontrol" | "flow_ctrl" | "flow_control" => {
68                        match &*val {
69                            "none" | "no" | "off" => builder.flow_control(FlowControl::None),
70                            "soft" | "sw" | "software" => {
71                                builder.flow_control(FlowControl::Software)
72                            }
73                            "hard" | "hw" | "hardware" => {
74                                builder.flow_control(FlowControl::Hardware)
75                            }
76                            _ => builder,
77                        }
78                    }
79                    "parity" => match &*val {
80                        "none" | "no" => builder.parity(Parity::None),
81                        "odd" | "od" => builder.parity(Parity::Odd),
82                        "even" | "evn" | "ev" => builder.parity(Parity::Even),
83                        _ => builder,
84                    },
85                    "stop" | "stopbits" | "stop_bits" => match &*val {
86                        "1" | "one" => builder.stop_bits(StopBits::One),
87                        "2" | "two" => builder.stop_bits(StopBits::Two),
88                        _ => builder,
89                    },
90                    _ => builder,
91                });
92            let url = url.clone();
93            Some(Arc::new(SerialConnector { url, builder }))
94        } else {
95            None
96        }
97    }
98}
99
100#[derive(Clone)]
101struct SerialConnector {
102    url: Url,
103    builder: SerialPortBuilder,
104}
105
106impl Connector for SerialConnector {
107    fn url(&self) -> &Url {
108        &self.url
109    }
110
111    fn connect(&self) -> BoxedConnect {
112        let this = self.clone();
113        Box::pin(async move {
114            let mut stm = SerialStream::open(&this.builder)
115                .map_err(|err| Error::FailedConnect(err.to_string()))?;
116            #[cfg(unix)]
117            stm.set_exclusive(true)
118                .map_err(|err| Error::FailedConnect(err.to_string()))?;
119            Ok(Box::new(stm) as BoxedConnection)
120        })
121    }
122}