Index: .efiles ================================================================== --- .efiles +++ .efiles @@ -8,6 +8,7 @@ src/types/telegram.rs src/types/params.rs src/types/kvlines.rs src/types/validators.rs src/codec.rs +src/codec/utils.rs tests/*.rs Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,8 +1,8 @@ [package] name = "blather" -version = "0.10.0" +version = "0.10.1" edition = "2021" license = "0BSD" keywords = [ "line-based", "protocol", "tokio", "codec" ] repository = "https://repos.qrnch.tech/pub/blather" description = "A talkative line-based protocol" @@ -13,20 +13,19 @@ "rustfmt.toml", "www" ] [dependencies] -bytes = { version = "1.5.0" } +bytes = { version = "1.7.1" } futures = { version = "0.3.30" } -# Tempoarily add "net", because there's a rustdoc bug in tokio 1.36.0. -tokio = { version = "1.36.0", features = ["net"] } -tokio-util = { version= "0.7.10", features = ["codec"] } +tokio = { version = "1.39.2" } +tokio-stream = { version = "0.1.15" } +tokio-util = { version= "0.7.11", features = ["codec"] } [dev-dependencies] -tokio = { version = "1.36.0", features = ["macros", "net"] } -tokio-stream = { version = "0.1.14" } -tokio-test = { version = "0.4.3" } +tokio = { version = "1.39.2", features = ["macros", "net"] } +tokio-test = { version = "0.4.4" } [package.metadata.docs.rs] rustdoc-args = ["--generate-link-to-definition"] # vim: set ft=toml et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: README.md ================================================================== --- README.md +++ README.md @@ -1,5 +1,5 @@ # blather -A talkative, somwhat reminiscent to HTTP, line-based protocol, implemented as +A talkative, somwhat reminiscent of HTTP, line-based protocol, implemented as a tokio-util Codec. Index: src/codec.rs ================================================================== --- src/codec.rs +++ src/codec.rs @@ -1,7 +1,9 @@ //! A [`tokio_util::codec`] Codec that is used to encode and decode the //! blather protocol. + +pub mod utils; use std::{ fmt, {cmp, collections::HashMap, mem} }; ADDED src/codec/utils.rs Index: src/codec/utils.rs ================================================================== --- /dev/null +++ src/codec/utils.rs @@ -0,0 +1,67 @@ +//! Helpers used to perform common framed stream operation. + +use tokio::io::AsyncRead; + +use tokio_util::codec::Framed; + +use tokio_stream::StreamExt; + +use crate::{ + codec::{self, Codec}, + err::Error, + Telegram +}; + + +/// Validation callback result. +pub enum Outcome { + /// The Telegram should be returned to the application. + Accept, + + /// The Telefgram should be ignored. + Ignore, + + /// Tell the caller that a fatal error occurred. + Fail(Error) +} + +/// Receive a [`Telegram`] from a `Frame`'d stream. +/// +/// The callback in `validate` can be used to inspect the received `Telegram` +/// and choose whether to return/ignore it or return a fatal error. +/// +/// If the connection is closed this function will return `Ok(None)`. +pub async fn expect_telegram( + frmio: &mut Framed, + validate: F +) -> Result, Error> +where + T: AsyncRead + Unpin, + F: Fn(&Telegram) -> Outcome +{ + loop { + let Some(frm) = frmio.next().await else { + // Got None, which (probably) means the connection has ended. + break Ok(None); + }; + let frm = match frm { + Ok(frm) => frm, + Err(e) => { + break Err(Error::Protocol(e.to_string())); + } + }; + let codec::Input::Telegram(tg) = frm else { + // If a non-Telegram is received, then abort and close connection + break Err(Error::Protocol( + "Did not receive an expected Telegram".into() + )); + }; + match validate(&tg) { + Outcome::Accept => break Ok(Some(tg)), + Outcome::Ignore => continue, + Outcome::Fail(e) => break Err(e) + } + } +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: src/types/telegram.rs ================================================================== --- src/types/telegram.rs +++ src/types/telegram.rs @@ -484,10 +484,20 @@ params, ..Default::default() } } } + +impl TryFrom<(&str, Params)> for Telegram { + type Error = Error; + + fn try_from(t: (&str, Params)) -> Result { + let mut tg = Telegram::new_topic(t.0)?; + tg.params = t.1; + Ok(tg) + } +} impl From> for Telegram { fn from(hm: HashMap) -> Self { Telegram { params: Params::from(hm), Index: tests/telegram.rs ================================================================== --- tests/telegram.rs +++ tests/telegram.rs @@ -1,6 +1,7 @@ -use blather::{Error, Telegram}; +use blather::{Error, Params, Telegram}; + #[test] fn simple() { let mut msg = Telegram::new(); @@ -114,7 +115,16 @@ tg.set_topic("Some Topic"), Err(Error::BadFormat("Invalid topic character".to_string())) ); } + +#[test] +fn create_from_tuple() { + let mut params = Params::new(); + params.add_str("my", "word").unwrap(); + let tg = Telegram::try_from(("Hello", params)).unwrap(); + + assert_eq!(tg.get_str("my"), Some("word")); +} // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: www/changelog.md ================================================================== --- www/changelog.md +++ www/changelog.md @@ -2,18 +2,31 @@ ⚠️ indicates a breaking change. ## [Unreleased] -[Details](/vdiff?from=blather-0.10.0&to=trunk) +[Details](/vdiff?from=blather-0.10.1&to=trunk) ### Added ### Changed ### Removed +--- + +## [0.10.1] - 2024-08-06 + +[Details](/vdiff?from=blather-0.10.0&to=blather-0.10.1) + +### Added + +- Module `codec::utils` added for collecting `Frame`'d stream helpers. +- `codec::utils::expect_telegram()` can be used to receive `Telegram`s with + simple validation. +- Add `TryFrom<(&str, Params)>` implementation for `Telegram`. + --- ## [0.10.0] - 2024-02-23 [Details](/vdiff?from=blather-0.9.0&to=blather-0.10.0) Index: www/index.md ================================================================== --- www/index.md +++ www/index.md @@ -1,8 +1,8 @@ # blather -A talkative, somwhat reminiscent to HTTP, line-based protocol, implemented as +A talkative, somwhat reminiscent of HTTP, line-based protocol, implemented as a tokio-util Codec. ## Change log