unicom_serial/
tokio_impl.rs1use 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#[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}