playdate_device/serial/
mod.rs

1use std::borrow::Cow;
2use std::cell::RefCell;
3
4
5pub mod discover;
6mod blocking;
7mod r#async;
8
9mod methods;
10pub use methods::*;
11
12
13#[cfg(not(feature = "tokio-serial"))]
14type Port = Box<dyn serialport::SerialPort>;
15#[cfg(feature = "tokio-serial")]
16type Port = Box<tokio_serial::SerialStream>;
17
18pub struct Interface {
19	info: serialport::SerialPortInfo,
20	port: Option<RefCell<Port>>,
21}
22
23
24impl std::fmt::Display for Interface {
25	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26		use serialport::SerialPort;
27
28		let port_name = &self.info.port_name;
29		let name = self.port.as_ref().and_then(|p| {
30			                             p.try_borrow()
31			                              .ok()
32			                              .and_then(|p| p.name().filter(|s| s != port_name))
33		                             });
34
35		write!(f, "serial:{}", name.as_deref().unwrap_or(port_name))
36	}
37}
38
39impl std::fmt::Debug for Interface {
40	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41		f.debug_struct("Interface")
42		 .field("name", &self.info.port_name)
43		 .field("opened", &self.port.is_some())
44		 .finish()
45	}
46}
47
48
49impl Interface {
50	#[cfg_attr(feature = "tracing", tracing::instrument)]
51	pub fn new(info: serialport::SerialPortInfo) -> Self { Self { info, port: None } }
52
53	#[cfg_attr(feature = "tracing", tracing::instrument)]
54	pub fn new_with(port: Port, name: Option<String>) -> Self {
55		use serialport::{SerialPort, SerialPortType, SerialPortInfo};
56
57		let name = port.name().or(name).map(Cow::from).unwrap_or_default();
58		let info = SerialPortInfo { port_name: name.to_string(),
59		                            port_type: SerialPortType::Unknown };
60
61		let mut result = Self::new(info);
62		result.set_port(port);
63		result
64	}
65
66	pub fn info(&self) -> &serialport::SerialPortInfo { &self.info }
67	pub fn is_open(&self) -> bool { self.port.is_some() }
68
69	#[cfg_attr(feature = "tracing", tracing::instrument)]
70	pub fn set_port(&mut self, port: Port) { self.port = Some(RefCell::new(port)); }
71
72	#[cfg_attr(feature = "tracing", tracing::instrument)]
73	pub fn open(&mut self) -> Result<(), serialport::Error> {
74		if self.port.is_some() {
75			Ok(())
76		} else {
77			let port = open(&self.info.port_name).map(RefCell::new)?;
78			self.port = Some(port);
79			Ok(())
80		}
81	}
82
83
84	#[cfg_attr(feature = "tracing", tracing::instrument)]
85	pub fn close(&mut self) { self.port.take(); }
86}
87
88
89#[cfg_attr(feature = "tracing", tracing::instrument)]
90pub fn open<'a, S>(port_name: S) -> Result<Port, serialport::Error>
91	where S: Into<std::borrow::Cow<'a, str>> + std::fmt::Debug {
92	trace!("opening port {port_name:?}");
93	let builder = port_builder(port_name);
94
95	let port;
96	#[cfg(not(feature = "tokio-serial"))]
97	{
98		port = builder.open()?;
99	}
100	#[cfg(feature = "tokio-serial")]
101	{
102		use tokio_serial::SerialPortBuilderExt;
103		port = builder.open_native_async().map(Box::new)?;
104	}
105
106	{
107		use serialport::SerialPort;
108		let name = port.as_ref().name();
109		let name = name.as_deref().unwrap_or("n/a");
110		trace!("opened port: {name}");
111	}
112	Ok(port)
113}
114
115fn port_builder<'a>(port_name: impl Into<std::borrow::Cow<'a, str>>) -> serialport::SerialPortBuilder {
116	serialport::new(port_name, 115200).data_bits(serialport::DataBits::Eight)
117}
118
119
120/* NOTE: This can be safely sent between thread, but not inner port,
121			but that's okay because it's boxen under `RefCell`.
122			Probably should be pinned, but not sure yet.
123*/
124unsafe impl Send for Interface {}
125unsafe impl Sync for Interface {}