mirror of https://github.com/fafhrd91/actix-web
Compare commits
No commits in common. "d604bcc85140e6abe2d4e5488a05116a394be0ae" and "8c397f83a3c391aefbab5cba2deefdfe27ffcb03" have entirely different histories.
d604bcc851
...
8c397f83a3
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- Add support for extracting multi-component path params into a sequence (Vec, tuple, ...). [#3432]
|
|
||||||
|
|
||||||
[#3432]: https://github.com/actix/actix-web/pull/3432
|
|
||||||
|
|
||||||
## 0.5.4
|
## 0.5.4
|
||||||
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.88.
|
- Minimum supported Rust version (MSRV) is now 1.88.
|
||||||
|
|
|
||||||
|
|
@ -399,25 +399,11 @@ impl<'de> Deserializer<'de> for Value<'de> {
|
||||||
visitor.visit_newtype_struct(self)
|
visitor.visit_newtype_struct(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
|
fn deserialize_tuple<V>(self, _: usize, _: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: Visitor<'de>,
|
V: Visitor<'de>,
|
||||||
{
|
{
|
||||||
let value_seq = ValueSeq::new(self.value);
|
Err(de::value::Error::custom("unsupported type: tuple"))
|
||||||
if len == value_seq.len() {
|
|
||||||
visitor.visit_seq(value_seq)
|
|
||||||
} else {
|
|
||||||
Err(de::value::Error::custom(
|
|
||||||
"path and tuple lengths don't match",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
visitor.visit_seq(ValueSeq::new(self.value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_struct<V>(
|
fn deserialize_struct<V>(
|
||||||
|
|
@ -435,13 +421,13 @@ impl<'de> Deserializer<'de> for Value<'de> {
|
||||||
fn deserialize_tuple_struct<V>(
|
fn deserialize_tuple_struct<V>(
|
||||||
self,
|
self,
|
||||||
_: &'static str,
|
_: &'static str,
|
||||||
len: usize,
|
_: usize,
|
||||||
visitor: V,
|
_: V,
|
||||||
) -> Result<V::Value, Self::Error>
|
) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: Visitor<'de>,
|
V: Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_tuple(len, visitor)
|
Err(de::value::Error::custom("unsupported type: tuple struct"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
|
|
@ -477,6 +463,7 @@ impl<'de> Deserializer<'de> for Value<'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsupported_type!(deserialize_seq, "seq");
|
||||||
unsupported_type!(deserialize_map, "map");
|
unsupported_type!(deserialize_map, "map");
|
||||||
unsupported_type!(deserialize_identifier, "identifier");
|
unsupported_type!(deserialize_identifier, "identifier");
|
||||||
}
|
}
|
||||||
|
|
@ -546,43 +533,6 @@ impl<'de> de::VariantAccess<'de> for UnitVariant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ValueSeq<'de> {
|
|
||||||
elems: std::str::Split<'de, char>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> ValueSeq<'de> {
|
|
||||||
fn new(value: &'de str) -> Self {
|
|
||||||
Self {
|
|
||||||
elems: value.split('/'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.elems.clone().filter(|s| !s.is_empty()).count()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> de::SeqAccess<'de> for ValueSeq<'de> {
|
|
||||||
type Error = de::value::Error;
|
|
||||||
|
|
||||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
|
|
||||||
where
|
|
||||||
T: de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
for elem in &mut self.elems {
|
|
||||||
if !elem.is_empty() {
|
|
||||||
return seed.deserialize(Value { value: elem }).map(Some);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> Option<usize> {
|
|
||||||
Some(self.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
@ -617,24 +567,6 @@ mod tests {
|
||||||
val: TestEnum,
|
val: TestEnum,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct TestSeq1 {
|
|
||||||
tail: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct TestSeq2 {
|
|
||||||
tail: (String, String, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct TestSeq3 {
|
|
||||||
tail: TestTupleStruct,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
|
||||||
struct TestTupleStruct(String, String, String);
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_request_extract() {
|
fn test_request_extract() {
|
||||||
let mut router = Router::<()>::build();
|
let mut router = Router::<()>::build();
|
||||||
|
|
@ -730,62 +662,6 @@ mod tests {
|
||||||
assert!(format!("{:?}", i).contains("unknown variant"));
|
assert!(format!("{:?}", i).contains("unknown variant"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extract_seq() {
|
|
||||||
let mut router = Router::<()>::build();
|
|
||||||
router.path("/path/to/{tail}*", ());
|
|
||||||
let router = router.finish();
|
|
||||||
|
|
||||||
let mut path = Path::new("/path/to/tail/with/slash%2fes");
|
|
||||||
assert!(router.recognize(&mut path).is_some());
|
|
||||||
|
|
||||||
let i: (String,) = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
|
|
||||||
assert_eq!(i.0, String::from("tail/with/slash/es"));
|
|
||||||
|
|
||||||
let i: TestSeq1 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
i.tail,
|
|
||||||
vec![
|
|
||||||
String::from("tail"),
|
|
||||||
String::from("with"),
|
|
||||||
String::from("slash/es")
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let i: TestSeq2 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
i.tail,
|
|
||||||
(
|
|
||||||
String::from("tail"),
|
|
||||||
String::from("with"),
|
|
||||||
String::from("slash/es")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let i: TestSeq3 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
i.tail,
|
|
||||||
TestTupleStruct(
|
|
||||||
String::from("tail"),
|
|
||||||
String::from("with"),
|
|
||||||
String::from("slash/es")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_value_seq_size_hint_counts_remaining_elements() {
|
|
||||||
use serde::de::SeqAccess as _;
|
|
||||||
|
|
||||||
let mut seq = ValueSeq::new("tail/with/slash");
|
|
||||||
|
|
||||||
assert_eq!(seq.size_hint(), Some(3));
|
|
||||||
|
|
||||||
let elem = seq.next_element::<String>().unwrap();
|
|
||||||
assert_eq!(elem.as_deref(), Some("tail"));
|
|
||||||
assert_eq!(seq.size_hint(), Some(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_errors() {
|
fn test_extract_errors() {
|
||||||
let mut router = Router::<()>::build();
|
let mut router = Router::<()>::build();
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,7 @@ impl HttpRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method returns mutable reference to the request head.
|
/// This method returns mutable reference to the request head.
|
||||||
///
|
/// panics if multiple references of HTTP request exists.
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if multiple references of HTTP request exists.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
|
pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
|
||||||
&mut Rc::get_mut(&mut self.inner).unwrap().head
|
&mut Rc::get_mut(&mut self.inner).unwrap().head
|
||||||
|
|
@ -109,12 +106,6 @@ impl HttpRequest {
|
||||||
/// Reconstructed URL is best-effort, using [`connection_info`](HttpRequest::connection_info())
|
/// Reconstructed URL is best-effort, using [`connection_info`](HttpRequest::connection_info())
|
||||||
/// to get forwarded scheme & host.
|
/// to get forwarded scheme & host.
|
||||||
///
|
///
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics when the reconstructed URL cannot be parsed, such as when the host is malformed.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_web::test::TestRequest;
|
/// use actix_web::test::TestRequest;
|
||||||
/// let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
|
/// let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
|
||||||
|
|
|
||||||
|
|
@ -53,26 +53,6 @@ use crate::{
|
||||||
/// format!("Welcome {}!", info.name)
|
/// format!("Welcome {}!", info.name)
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// Segments matching multiple path components can be deserialized
|
|
||||||
/// into a `Vec<_>` to percent-decode the components individually. Empty
|
|
||||||
/// path components are ignored.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use actix_web::{get, web};
|
|
||||||
/// use serde::Deserialize;
|
|
||||||
///
|
|
||||||
/// #[derive(Deserialize)]
|
|
||||||
/// struct Tail {
|
|
||||||
/// tail: Vec<String>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // extract `Tail` from a path using serde
|
|
||||||
/// #[get("/path/to/{tail}*")]
|
|
||||||
/// async fn index(info: web::Path<Tail>) -> String {
|
|
||||||
/// format!("Navigating to {}!", info.tail.join(" :: "))
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut, AsRef, Display, From)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut, AsRef, Display, From)]
|
||||||
pub struct Path<T>(T);
|
pub struct Path<T>(T);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue