feat: add async support

This commit is contained in:
2026-02-25 18:36:06 +01:00
parent 277f2200b7
commit 91b19a803e
4 changed files with 88 additions and 2 deletions

3
CHANGELOG.md Normal file
View File

@@ -0,0 +1,3 @@
# v0.2.0
* Added async client support behind a non-default cargo feature flag using tokio's `AsyncRead` and `AsyncWrite`.

18
Cargo.lock generated
View File

@@ -273,13 +273,20 @@ dependencies = [
[[package]]
name = "pamsock"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"anstyle",
"clap",
"pam",
"tokio",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -367,6 +374,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
dependencies = [
"pin-project-lite",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"

View File

@@ -1,13 +1,18 @@
[package]
name = "pamsock"
version = "0.1.0"
version = "0.2.0"
edition = "2024"
[features]
async = ["dep:tokio"]
[dependencies]
anstyle = "1.0.13"
clap = { version = "4.5.60", features = ["derive"] }
pam = "0.8.0"
tokio = { version = "1.49.0", optional = true }
[lints.clippy]
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }

View File

@@ -80,6 +80,19 @@ pub mod prot {
ServerResponse::from_u8(ucom::read_u8(&mut stream)?)
}
#[must_use]
#[cfg(feature = "async")]
pub async fn attempt_login_async(
mut stream: impl tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
user: &str,
passwd: &str,
) -> Option<ServerResponse> {
ucom_async::write_str(&mut stream, user).await?;
ucom_async::write_str(&mut stream, passwd).await?;
ServerResponse::from_u8(ucom_async::read_u8(&mut stream).await?)
}
pub mod ucom {
//! # µcom
//!
@@ -123,4 +136,53 @@ pub mod prot {
into.write_all(&size_buf).ok()
}
}
#[cfg(feature = "async")]
pub mod ucom_async {
//! # µcom_async
//!
//! Analogous to [`super::ucom`] but asynchronous.
use tokio::io::{AsyncRead, AsyncWrite, AsyncReadExt, AsyncWriteExt};
pub async fn read_duple(from: &mut (impl AsyncRead + Unpin)) -> Option<(String, String)> {
let user = read_str(from).await?;
let pass = read_str(from).await?;
Some((user, pass))
}
pub async fn read_str(from: &mut (impl AsyncRead + Unpin)) -> Option<String> {
let size = read_u8(from).await? as usize;
let mut buf = vec![0; size];
from.read_exact(&mut buf).await.ok()?;
String::from_utf8(buf).ok()
}
pub async fn read_u8(from: &mut (impl AsyncRead + Unpin)) -> Option<u8> {
let mut size_buf = [0; 1];
from.read_exact(&mut size_buf).await.ok()?;
Some(u8::from_be_bytes(size_buf))
}
pub async fn write_duple(
into: &mut (impl AsyncWrite + Unpin),
user: &str,
pass: &str,
) -> Option<()> {
write_str(into, user).await?;
write_str(into, pass).await?;
Some(())
}
pub async fn write_str(into: &mut (impl AsyncWrite + Unpin), str: &str) -> Option<()> {
let buf = str.as_bytes();
write_u8(into, buf.len().try_into().ok()?).await?;
into.write_all(buf).await.ok()
}
pub async fn write_u8(into: &mut (impl AsyncWrite + Unpin), u8: u8) -> Option<()> {
let size_buf = u8.to_be_bytes();
into.write_all(&size_buf).await.ok()
}
}
}