Skip to content

Commit d46d8d4

Browse files
committed
Refactor ParseError
Instead of the `crate::Error`, the <private> method `stream::parse_headers` will return the specialized error with kind enumerating the possible causes of parsing errors. This change will help to specialize errors error returned by public methods. Along this change, I also refactored 2 errors, namely the `ParseHeaderNameError` and the `ParseHeaderValueError` to convert them to specialization of generic `crate::error::Error<_>`. These changes are collected in the same commit because all of them describe errors occurred during either building or parsing headers.
1 parent 73e0aa2 commit d46d8d4

3 files changed

Lines changed: 83 additions & 40 deletions

File tree

async-nats/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ where
7373

7474
/// Enables wrapping source errors to the crate-specific error type
7575
/// by additionally specifying the kind of the target error.
76-
trait WithKind<Kind>
76+
pub(crate) trait WithKind<Kind>
7777
where
7878
Kind: Clone + Debug + Display + PartialEq,
7979
Self: Into<crate::Error>,

async-nats/src/header.rs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
2323
use std::{collections::HashMap, fmt, slice::Iter, str::FromStr};
2424

25+
use crate::error::Error;
2526
use bytes::Bytes;
2627
use serde::{Deserialize, Serialize};
2728

@@ -312,7 +313,7 @@ impl FromStr for HeaderValue {
312313

313314
fn from_str(s: &str) -> Result<Self, Self::Err> {
314315
if s.contains(['\r', '\n']) {
315-
return Err(ParseHeaderValueError);
316+
return Err(s.into());
316317
}
317318

318319
Ok(HeaderValue {
@@ -339,19 +340,26 @@ impl HeaderValue {
339340
}
340341
}
341342

342-
#[derive(Debug, Clone)]
343-
pub struct ParseHeaderValueError;
343+
#[derive(Clone, Debug, PartialEq)]
344+
pub struct ParseHeaderValueErrorKind(String);
344345

345-
impl fmt::Display for ParseHeaderValueError {
346-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346+
impl fmt::Display for ParseHeaderValueErrorKind {
347+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347348
write!(
348349
f,
349-
r#"invalid character found in header value (value cannot contain '\r' or '\n')"#
350+
r#"invalid character found in header value (value cannot contain '\r' or '\n'): {:?}"#,
351+
self.0
350352
)
351353
}
352354
}
353355

354-
impl std::error::Error for ParseHeaderValueError {}
356+
pub type ParseHeaderValueError = Error<ParseHeaderValueErrorKind>;
357+
358+
impl From<&str> for ParseHeaderValueError {
359+
fn from(value: &str) -> Self {
360+
Self::new(ParseHeaderValueErrorKind(value.into()))
361+
}
362+
}
355363

356364
pub trait IntoHeaderName {
357365
fn into_header_name(self) -> HeaderName;
@@ -568,7 +576,9 @@ impl FromStr for HeaderName {
568576

569577
fn from_str(s: &str) -> Result<Self, Self::Err> {
570578
if s.contains(|c: char| c == ':' || (c as u8) < 33 || (c as u8) > 126) {
571-
return Err(ParseHeaderNameError);
579+
return Err(ParseHeaderNameError::new(ParseHeaderNameErrorKind(
580+
s.into(),
581+
)));
572582
}
573583

574584
match StandardHeader::from_bytes(s.as_ref()) {
@@ -600,16 +610,22 @@ impl AsRef<str> for HeaderName {
600610
}
601611
}
602612

603-
#[derive(Debug, Clone)]
604-
pub struct ParseHeaderNameError;
613+
#[derive(Clone, Debug, PartialEq)]
614+
pub struct ParseHeaderNameErrorKind(String);
605615

606-
impl std::fmt::Display for ParseHeaderNameError {
607-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
608-
write!(f, "invalid header name (name cannot contain non-ascii alphanumeric characters other than '-')")
616+
impl fmt::Display for ParseHeaderNameErrorKind {
617+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618+
write!(f, "invalid header name (name cannot contain non-ascii alphanumeric characters other than '-'): {:?}", self.0)
609619
}
610620
}
611621

612-
impl std::error::Error for ParseHeaderNameError {}
622+
pub type ParseHeaderNameError = Error<ParseHeaderNameErrorKind>;
623+
624+
impl From<&str> for ParseHeaderNameError {
625+
fn from(value: &str) -> Self {
626+
Self::new(ParseHeaderNameErrorKind(value.into()))
627+
}
628+
}
613629

614630
#[cfg(test)]
615631
mod tests {

async-nats/src/jetstream/stream.rs

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
1616
#[cfg(feature = "server_2_10")]
1717
use std::collections::HashMap;
18+
use std::fmt::Formatter;
1819
use std::{
1920
fmt::{self, Debug, Display},
2021
future::IntoFuture,
21-
io::{self, ErrorKind},
22+
io::ErrorKind,
2223
pin::Pin,
2324
str::FromStr,
2425
task::Poll,
@@ -1311,31 +1312,64 @@ fn is_continuation(c: char) -> bool {
13111312
}
13121313
const HEADER_LINE: &str = "NATS/1.0";
13131314

1315+
#[derive(Clone, Copy, Debug, PartialEq)]
1316+
pub enum ParseHeadersErrorKind {
1317+
HeaderInfoMissing,
1318+
InvalidHeader,
1319+
InvalidVersionLine,
1320+
InvalidStatusCode,
1321+
MalformedHeader,
1322+
}
1323+
1324+
impl Display for ParseHeadersErrorKind {
1325+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1326+
match self {
1327+
Self::InvalidHeader => write!(f, "invalid header"),
1328+
Self::InvalidStatusCode => write!(f, "invalid status code"),
1329+
Self::InvalidVersionLine => write!(f, "version line does not start with NATS/1.0"),
1330+
Self::HeaderInfoMissing => write!(f, "expected header information not found"),
1331+
Self::MalformedHeader => write!(f, "malformed header line"),
1332+
}
1333+
}
1334+
}
1335+
1336+
pub type ParseHeadersError = Error<ParseHeadersErrorKind>;
1337+
1338+
impl From<ParseHeaderNameError> for ParseHeadersError {
1339+
fn from(e: ParseHeaderNameError) -> Self {
1340+
e.with_kind(ParseHeadersErrorKind::MalformedHeader)
1341+
}
1342+
}
1343+
1344+
impl From<ParseHeaderValueError> for ParseHeadersError {
1345+
fn from(e: ParseHeaderValueError) -> Self {
1346+
e.with_kind(ParseHeadersErrorKind::MalformedHeader)
1347+
}
1348+
}
1349+
1350+
impl From<InvalidStatusCode> for ParseHeadersError {
1351+
fn from(e: InvalidStatusCode) -> Self {
1352+
e.with_kind(ParseHeadersErrorKind::InvalidStatusCode)
1353+
}
1354+
}
1355+
13141356
#[allow(clippy::type_complexity)]
13151357
fn parse_headers(
13161358
buf: &[u8],
1317-
) -> Result<(Option<HeaderMap>, Option<StatusCode>, Option<String>), crate::Error> {
1359+
) -> Result<(Option<HeaderMap>, Option<StatusCode>, Option<String>), ParseHeadersError> {
13181360
let mut headers = HeaderMap::new();
13191361
let mut maybe_status: Option<StatusCode> = None;
13201362
let mut maybe_description: Option<String> = None;
13211363
let mut lines = if let Ok(line) = std::str::from_utf8(buf) {
13221364
line.lines().peekable()
13231365
} else {
1324-
return Err(Box::new(std::io::Error::new(
1325-
ErrorKind::Other,
1326-
"invalid header",
1327-
)));
1366+
return Err(ParseHeadersErrorKind::InvalidHeader.into());
13281367
};
13291368

13301369
if let Some(line) = lines.next() {
13311370
let line = line
13321371
.strip_prefix(HEADER_LINE)
1333-
.ok_or_else(|| {
1334-
Box::new(std::io::Error::new(
1335-
ErrorKind::Other,
1336-
"version line does not start with NATS/1.0",
1337-
))
1338-
})?
1372+
.ok_or_else(|| ParseHeadersError::new(ParseHeadersErrorKind::InvalidHeader))?
13391373
.trim();
13401374

13411375
match line.split_once(' ') {
@@ -1355,10 +1389,7 @@ fn parse_headers(
13551389
}
13561390
}
13571391
} else {
1358-
return Err(Box::new(std::io::Error::new(
1359-
ErrorKind::Other,
1360-
"expected header information not found",
1361-
)));
1392+
return Err(ParseHeadersErrorKind::HeaderInfoMissing.into());
13621393
};
13631394

13641395
while let Some(line) = lines.next() {
@@ -1373,16 +1404,9 @@ fn parse_headers(
13731404
s.push_str(v.trim());
13741405
}
13751406

1376-
headers.insert(
1377-
HeaderName::from_str(k)?,
1378-
HeaderValue::from_str(&s)
1379-
.map_err(|err| Box::new(io::Error::new(ErrorKind::Other, err)))?,
1380-
);
1407+
headers.insert(HeaderName::from_str(k)?, HeaderValue::from_str(&s)?);
13811408
} else {
1382-
return Err(Box::new(std::io::Error::new(
1383-
ErrorKind::Other,
1384-
"malformed header line",
1385-
)));
1409+
return Err(ParseHeadersErrorKind::MalformedHeader.into());
13861410
}
13871411
}
13881412

@@ -1528,6 +1552,9 @@ pub struct External {
15281552
pub delivery_prefix: Option<String>,
15291553
}
15301554

1555+
use crate::error::WithKind;
1556+
use crate::header::{ParseHeaderNameError, ParseHeaderValueError};
1557+
use crate::status::InvalidStatusCode;
15311558
use std::marker::PhantomData;
15321559

15331560
#[derive(Debug, Default)]

0 commit comments

Comments
 (0)