Skip to content

Commit

Permalink
bump: v0.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
fundon committed Dec 14, 2023
1 parent d14b310 commit 7012881
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 41 deletions.
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ members = [
]

[workspace.package]
version = "0.5.2"
version = "0.6.0"
authors = ["Fangdun Tsai <[email protected]>"]
edition = "2021"
homepage = "https://viz.rs"
Expand All @@ -45,12 +45,12 @@ license = "MIT"
rust-version = "1.63" # follows `tokio` and `hyper`

[workspace.dependencies]
viz = { version = "0.5.2", path = "viz" }
viz-core = { version = "0.5.2", path = "viz-core" }
viz-router = { version = "0.5.2", path = "viz-router" }
viz-handlers = { version = "0.5.2", path = "viz-handlers", default-features = false }
viz-macros = { version = "0.1", path = "viz-macros" }
viz-test = { version = "0.1", path = "viz-test" }
viz = { version = "0.6.0", path = "viz" }
viz-core = { version = "0.6.0", path = "viz-core" }
viz-router = { version = "0.6.0", path = "viz-router" }
viz-handlers = { version = "0.6.0", path = "viz-handlers", default-features = false }
viz-macros = { version = "0.2.0", path = "viz-macros" }
viz-test = { version = "0.1.0", path = "viz-test" }

async-trait = "0.1"
dyn-clone = "1.0"
Expand Down
2 changes: 2 additions & 0 deletions viz-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ default = [
"websocket",
"cookie",
"session",
"fs"
]

state = []
Expand All @@ -37,6 +38,7 @@ query = ["dep:serde", "dep:serde_urlencoded"]
multipart = ["dep:form-data"]
websocket = ["dep:tokio-tungstenite", "tokio/rt"]
sse = ["dep:tokio-stream", "tokio/time"]
fs = ["dep:tokio-util", "tokio/fs"]

cookie = ["dep:cookie"]
cookie-private = ["cookie", "cookie?/private"]
Expand Down
6 changes: 3 additions & 3 deletions viz-core/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use futures_util::{Stream, TryStreamExt};
use http_body_util::{combinators::BoxBody, BodyExt, Full, StreamBody};
use hyper::body::{Body, Frame, Incoming, SizeHint};

use crate::{Bytes, Error, Result};
use crate::{BoxError, Bytes, Error, Result};

/// The incoming body from HTTP [`Request`].
///
Expand Down Expand Up @@ -84,7 +84,7 @@ impl Body for IncomingBody {
}

impl Stream for IncomingBody {
type Item = Result<Bytes, Box<dyn std::error::Error + Send + Sync + 'static>>;
type Item = Result<Bytes, BoxError>;

#[inline]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Expand Down Expand Up @@ -138,7 +138,7 @@ impl OutgoingBody {
StreamBody::new(
stream
.map_ok(Into::into)
.map_ok(Frame::<Bytes>::data)
.map_ok(Frame::data)
.map_err(Into::into),
)
.boxed(),
Expand Down
78 changes: 52 additions & 26 deletions viz-core/src/response.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use futures_util::Stream;
use http_body_util::Full;

use crate::{header, Bytes, Error, OutgoingBody, Response, Result, StatusCode};
use crate::{async_trait, header, Bytes, Error, OutgoingBody, Response, Result, StatusCode};

/// The [`Response`] Extension.
#[async_trait]
pub trait ResponseExt: Sized {
/// Get the size of this response's body.
fn content_length(&self) -> Option<u64>;
Expand All @@ -17,19 +18,24 @@ pub trait ResponseExt: Sized {
K: header::AsHeaderName,
T: std::str::FromStr;

/// The response was successful (status in the range [`200-299`][mdn]) or not.
///
/// [mdn]: <https://developer.mozilla.org/en-US/docs/Web/API/Response/ok>
fn ok(&self) -> bool;

/// The response with the specified [`Content-Type`][mdn].
///
/// [mdn]: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type>
fn with<B>(body: B, content_type: &'static str) -> Response
where
B: Into<OutgoingBody>,
{
let mut res = Response::new(body.into());
res.headers_mut().insert(
let mut resp = Response::new(body.into());
resp.headers_mut().insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static(content_type),
);
res
resp
}

/// The response with `text/plain; charset=utf-8` media type.
Expand Down Expand Up @@ -82,20 +88,18 @@ pub trait ResponseExt: Sized {
Response::new(OutgoingBody::streaming(stream))
}

// TODO: Download transfers the file from path as an attachment.
// fn download() -> Response<Body>

/// The response was successful (status in the range [`200-299`][mdn]) or not.
///
/// [mdn]: <https://developer.mozilla.org/en-US/docs/Web/API/Response/ok>
fn ok(&self) -> bool;
/// Downloads transfers the file from path as an attachment.
#[cfg(feature = "fs")]
async fn download<T>(path: T, name: Option<&str>) -> Result<Self>
where
T: AsRef<std::path::Path> + Send;

/// The [`Content-Disposition`][mdn] header indicates if the content is expected to be
/// displayed inline in the browser, that is, as a Web page or as part of a Web page,
/// or as an attachment, that is downloaded and saved locally.
///
/// [mdn]: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition>
fn attachment(file: &str) -> Self;
fn attachment(value: &str) -> Self;

/// The [`Content-Location`][mdn] header indicates an alternate location for the returned data.
///
Expand Down Expand Up @@ -149,6 +153,7 @@ pub trait ResponseExt: Sized {
}
}

#[async_trait]
impl ResponseExt for Response {
fn content_length(&self) -> Option<u64> {
self.headers()
Expand Down Expand Up @@ -176,12 +181,33 @@ impl ResponseExt for Response {
self.status().is_success()
}

fn attachment(file: &str) -> Self {
let mut res = Self::default();
let val = header::HeaderValue::from_str(file)
#[cfg(feature = "fs")]
async fn download<T>(path: T, name: Option<&str>) -> Result<Self>
where
T: AsRef<std::path::Path> + Send,
{
let value = if let Some(filename) = name {
filename
} else if let Some(filename) = path.as_ref().file_name().and_then(std::ffi::OsStr::to_str) {
filename
} else {
"download"
}
.escape_default();

let mut resp = Self::attachment(&format!("attachment; filename=\"{value}\""));
*resp.body_mut() = OutgoingBody::streaming(tokio_util::io::ReaderStream::new(
tokio::fs::File::open(path).await.map_err(Error::from)?,
));
Ok(resp)
}

fn attachment(value: &str) -> Self {
let val = header::HeaderValue::from_str(value)
.expect("content-disposition is not the correct value");
res.headers_mut().insert(header::CONTENT_DISPOSITION, val);
res
let mut resp = Self::default();
resp.headers_mut().insert(header::CONTENT_DISPOSITION, val);
resp
}

fn location<T>(location: T) -> Self
Expand All @@ -190,9 +216,9 @@ impl ResponseExt for Response {
{
let val = header::HeaderValue::try_from(location.as_ref())
.expect("location is not the correct value");
let mut res = Self::default();
res.headers_mut().insert(header::CONTENT_LOCATION, val);
res
let mut resp = Self::default();
resp.headers_mut().insert(header::CONTENT_LOCATION, val);
resp
}

fn redirect<T>(url: T) -> Self
Expand All @@ -201,9 +227,9 @@ impl ResponseExt for Response {
{
let val =
header::HeaderValue::try_from(url.as_ref()).expect("url is not the correct value");
let mut res = Self::default();
res.headers_mut().insert(header::LOCATION, val);
res
let mut resp = Self::default();
resp.headers_mut().insert(header::LOCATION, val);
resp
}

fn redirect_with_status<T>(url: T, status: StatusCode) -> Self
Expand All @@ -212,8 +238,8 @@ impl ResponseExt for Response {
{
assert!(status.is_redirection(), "not a redirection status code");

let mut res = Self::redirect(url);
*res.status_mut() = status;
res
let mut resp = Self::redirect(url);
*resp.status_mut() = status;
resp
}
}
7 changes: 4 additions & 3 deletions viz-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ mod route_info;
pub use route_info::RouteInfo;

mod header;
mod payload;
mod realip;

pub use header::{Header, HeaderError};

mod payload;
pub use payload::{Payload, PayloadError};

mod realip;
pub use realip::RealIp;
4 changes: 2 additions & 2 deletions viz-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "viz-macros"
version = "0.1.0"
version = "0.2.0"
edition.workspace = true
license.workspace = true
authors.workspace = true
Expand All @@ -17,7 +17,7 @@ categories = ["asynchronous", "network-programming", "web-programming"]
proc-macro = true

[dependencies]
syn = "2.0"
syn = { version = "2.0", features = ["full"] }
quote = "1.0"

[dev-dependencies]
Expand Down
21 changes: 21 additions & 0 deletions viz-test/tests/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ async fn response_ext_with_server() -> Result<()> {
Full::new("<xml/>".into()),
mime::TEXT_XML.as_ref(),
))
})
.get("/download", |_: Request| {
let dir = std::env::var("CARGO_MANIFEST_DIR")
.map(std::path::PathBuf::from)
.unwrap();
Response::download(dir.join("README.md"), Some("file.txt"))
});

let client = TestServer::new(router).await?;
Expand All @@ -148,5 +154,20 @@ async fn response_ext_with_server() -> Result<()> {
assert_eq!(resp.content_length(), Some(6));
assert_eq!(resp.text().await.map_err(Error::boxed)?, "<xml/>");

let resp = client.get("/download").send().await.map_err(Error::boxed)?;
assert_eq!(resp.content_length(), None);
assert_eq!(
resp.headers().get(http::header::CONTENT_DISPOSITION),
Some(http::header::HeaderValue::from_static(
"attachment; filename=\"file.txt\""
))
.as_ref()
);
assert!(resp
.text()
.await
.map_err(Error::boxed)?
.starts_with("<p align=\"center\">"));

Ok(())
}
2 changes: 2 additions & 0 deletions viz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ default = [
"multipart",
"cookie",
"session",
"fs",
]

http1 = ["hyper/http1"]
Expand All @@ -38,6 +39,7 @@ params = ["viz-core/params"]
multipart = ["viz-core/multipart"]
websocket = ["viz-core/websocket"]
sse = ["viz-core/sse"]
fs = ["viz-core/fs"]

cookie = ["viz-core/cookie"]
cookie-private = ["viz-core/cookie-private"]
Expand Down

0 comments on commit 7012881

Please sign in to comment.