use std::{net::IpAddr, sync::Arc};
use pyo3::{PyResult, Python, exceptions::PyOSError, pyclass, pymethods};
use pyo3_async_runtimes::tokio::future_into_py;
use crate::{PyFut, py_value_err, sockaddr_as_tuple};
#[pyclass(frozen, module = "_internal")]
pub struct TcpListener {
pub(crate) listener: Arc<ts::TcpListener>,
}
#[pyclass(frozen, module = "_internal")]
pub struct TcpStream {
pub(crate) sock: Arc<ts::TcpStream>,
}
#[pymethods]
impl TcpListener {
pub fn accept<'p>(&self, py: Python<'p>) -> PyFut<'p> {
let sock = Arc::clone(&self.listener);
future_into_py(py, async move {
let stream = sock.accept().await.map_err(py_value_err)?;
Ok(TcpStream {
sock: Arc::new(stream),
})
})
}
pub fn local_addr(&self) -> (IpAddr, u16) {
sockaddr_as_tuple(self.listener.local_addr())
}
fn __repr__(&self) -> String {
format!("tailscale.TcpListener({})", self.listener.local_addr(),)
}
}
#[pymethods]
impl TcpStream {
pub fn send<'p>(&self, py: Python<'p>, msg: &[u8]) -> PyFut<'p> {
let sock = Arc::clone(&self.sock);
let msg = msg.to_vec();
future_into_py(py, async move {
let n = sock.send(&msg).await.map_err(py_value_err)?;
Ok(n)
})
}
pub fn recv<'p>(&self, py: Python<'p>) -> PyFut<'p> {
let sock = Arc::clone(&self.sock);
future_into_py(py, async move {
let ret = sock.recv_bytes().await.map_err(py_value_err)?;
Ok(ret)
})
}
pub fn local_addr(&self) -> (IpAddr, u16) {
sockaddr_as_tuple(self.sock.local_addr())
}
pub fn remote_addr(&self) -> (IpAddr, u16) {
sockaddr_as_tuple(self.sock.remote_addr())
}
fn __repr__(&self) -> String {
format!(
"tailscale.TcpStream({} -> {})",
self.sock.local_addr(),
self.sock.remote_addr()
)
}
pub fn writable(&self) -> bool {
true
}
pub fn readable(&self) -> bool {
true
}
pub fn seekable(&self) -> bool {
false
}
pub fn tell(&self) -> usize {
0
}
pub fn isatty(&self) -> bool {
false
}
pub fn fileno(&self) -> PyResult<i32> {
Err(PyOSError::new_err(
"tailscale.TcpStream does not use a file descriptor",
))
}
}