mirror of
https://github.com/rwf2/Rocket.git
synced 2025-01-17 23:19:06 +00:00
Use MediaType instead of ContentType for Route format.
This commit is contained in:
parent
9160483554
commit
1fb1cdfc58
@ -14,7 +14,7 @@ use syntax::ext::build::AstBuilder;
|
||||
use syntax::parse::token;
|
||||
use syntax::ptr::P;
|
||||
|
||||
use rocket::http::{Method, ContentType};
|
||||
use rocket::http::{Method, MediaType};
|
||||
|
||||
fn method_to_path(ecx: &ExtCtxt, method: Method) -> Path {
|
||||
quote_enum!(ecx, method => ::rocket::http::Method {
|
||||
@ -22,11 +22,10 @@ fn method_to_path(ecx: &ExtCtxt, method: Method) -> Path {
|
||||
})
|
||||
}
|
||||
|
||||
fn content_type_to_expr(ecx: &ExtCtxt, ct: Option<ContentType>) -> Option<P<Expr>> {
|
||||
fn media_type_to_expr(ecx: &ExtCtxt, ct: Option<MediaType>) -> Option<P<Expr>> {
|
||||
ct.map(|ct| {
|
||||
let (top, sub) = (ct.top().as_str(), ct.sub().as_str());
|
||||
quote_expr!(ecx, ::rocket::http::ContentType(
|
||||
::rocket::http::MediaType {
|
||||
quote_expr!(ecx, ::rocket::http::MediaType {
|
||||
source: None,
|
||||
top: ::rocket::http::IndexedStr::Concrete(
|
||||
::std::borrow::Cow::Borrowed($top)
|
||||
@ -35,8 +34,7 @@ fn content_type_to_expr(ecx: &ExtCtxt, ct: Option<ContentType>) -> Option<P<Expr
|
||||
::std::borrow::Cow::Borrowed($sub)
|
||||
),
|
||||
params: ::rocket::http::MediaParams::Static(&[])
|
||||
},
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -224,10 +222,10 @@ impl RouteGenerateExt for RouteParams {
|
||||
let path = &self.uri.node.as_str();
|
||||
let method = method_to_path(ecx, self.method.node);
|
||||
let format = self.format.as_ref().map(|kv| kv.value().clone());
|
||||
let content_type = option_as_expr(ecx, &content_type_to_expr(ecx, format));
|
||||
let media_type = option_as_expr(ecx, &media_type_to_expr(ecx, format));
|
||||
let rank = option_as_expr(ecx, &self.rank);
|
||||
|
||||
(path, method, content_type, rank)
|
||||
(path, method, media_type, rank)
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,7 +265,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
|
||||
// Generate and emit the static route info that uses the just generated
|
||||
// function as its handler. A proper Rocket route will be created from this.
|
||||
let struct_name = user_fn_name.prepend(ROUTE_STRUCT_PREFIX);
|
||||
let (path, method, content_type, rank) = route.explode(ecx);
|
||||
let (path, method, media_type, rank) = route.explode(ecx);
|
||||
let static_route_info_item = quote_item!(ecx,
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static $struct_name: ::rocket::StaticRouteInfo =
|
||||
@ -275,7 +273,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
|
||||
method: $method,
|
||||
path: $path,
|
||||
handler: $route_fn_name,
|
||||
format: $content_type,
|
||||
format: $media_type,
|
||||
rank: $rank,
|
||||
};
|
||||
).expect("static route info");
|
||||
|
@ -9,7 +9,7 @@ use utils::{span, MetaItemExt, SpanExt, is_valid_ident};
|
||||
use super::{Function, ParamIter};
|
||||
use super::keyvalue::KVSpanned;
|
||||
use super::uri::validate_uri;
|
||||
use rocket::http::{Method, ContentType};
|
||||
use rocket::http::{Method, MediaType};
|
||||
use rocket::http::uri::URI;
|
||||
|
||||
/// This structure represents the parsed `route` attribute.
|
||||
@ -25,7 +25,7 @@ pub struct RouteParams {
|
||||
pub uri: Spanned<URI<'static>>,
|
||||
pub data_param: Option<KVSpanned<Ident>>,
|
||||
pub query_param: Option<Spanned<Ident>>,
|
||||
pub format: Option<KVSpanned<ContentType>>,
|
||||
pub format: Option<KVSpanned<MediaType>>,
|
||||
pub rank: Option<KVSpanned<isize>>,
|
||||
}
|
||||
|
||||
@ -258,25 +258,25 @@ fn parse_rank(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> isize {
|
||||
-1
|
||||
}
|
||||
|
||||
fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> ContentType {
|
||||
fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> MediaType {
|
||||
if let LitKind::Str(ref s, _) = *kv.value() {
|
||||
if let Ok(ct) = ContentType::from_str(&s.as_str()) {
|
||||
if let Ok(ct) = MediaType::from_str(&s.as_str()) {
|
||||
if !ct.is_known() {
|
||||
let msg = format!("'{}' is not a known content-type", s);
|
||||
let msg = format!("'{}' is not a known media type", s);
|
||||
ecx.span_warn(kv.value.span, &msg);
|
||||
}
|
||||
|
||||
return ct;
|
||||
} else {
|
||||
ecx.span_err(kv.value.span, "malformed content-type");
|
||||
ecx.span_err(kv.value.span, "malformed media type");
|
||||
}
|
||||
}
|
||||
|
||||
ecx.struct_span_err(kv.span, r#"`format` must be a "content/type""#)
|
||||
ecx.struct_span_err(kv.span, r#"`format` must be a "media/type""#)
|
||||
.help(r#"format, if specified, must be a key-value pair where
|
||||
the key is `format` and the value is a string representing the
|
||||
content-type accepted. e.g: format = "application/json""#)
|
||||
media type accepted. e.g: format = "application/json""#)
|
||||
.emit();
|
||||
|
||||
ContentType::Any
|
||||
MediaType::Any
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ fn get1() -> &'static str { "hi" }
|
||||
#[get(path = "/", rank = "2")] //~ ERROR must be an int
|
||||
fn get2() -> &'static str { "hi" }
|
||||
|
||||
#[get(path = "/", format = 100)] //~ ERROR must be a "content/type"
|
||||
#[get(path = "/", format = 100)] //~ ERROR must be a "media/type"
|
||||
fn get3() -> &'static str { "hi" }
|
||||
|
||||
fn main() {
|
||||
|
@ -4,35 +4,35 @@
|
||||
extern crate rocket;
|
||||
|
||||
#[get("/", format = "applicationx-custom")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn one() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn two() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "//")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn three() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn four() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "a/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn five() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "/a")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn six() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "/a/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn seven() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "a/b/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "content/type"
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn eight() -> &'static str { "hi" }
|
||||
|
||||
fn main() { }
|
@ -3,13 +3,13 @@
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("/", format = "application/x-custom")] //~ WARNING not a known content-type
|
||||
#[get("/", format = "application/x-custom")] //~ WARNING not a known media type
|
||||
fn one() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "x-custom/plain")] //~ WARNING not a known content-type
|
||||
#[get("/", format = "x-custom/plain")] //~ WARNING not a known media type
|
||||
fn two() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known content-type
|
||||
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type
|
||||
fn three() -> &'static str { "hi" }
|
||||
|
||||
// Make the test fail here so we can actually check for the warnings above.
|
@ -1,10 +1,10 @@
|
||||
use handler::{Handler, ErrorHandler};
|
||||
use http::{Method, ContentType};
|
||||
use http::{Method, MediaType};
|
||||
|
||||
pub struct StaticRouteInfo {
|
||||
pub method: Method,
|
||||
pub path: &'static str,
|
||||
pub format: Option<ContentType>,
|
||||
pub format: Option<MediaType>,
|
||||
pub handler: Handler,
|
||||
pub rank: Option<isize>,
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::Route;
|
||||
|
||||
use http::uri::URI;
|
||||
use http::ContentType;
|
||||
use http::MediaType;
|
||||
use request::Request;
|
||||
|
||||
/// The Collider trait is used to determine if two items that can be routed on
|
||||
@ -71,8 +71,8 @@ impl<'a, 'b> Collider<URI<'b>> for URI<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Collider for ContentType {
|
||||
fn collides_with(&self, other: &ContentType) -> bool {
|
||||
impl Collider for MediaType {
|
||||
fn collides_with(&self, other: &MediaType) -> bool {
|
||||
let collide = |a, b| a == "*" || b == "*" || a == b;
|
||||
collide(self.top(), other.top()) && collide(self.sub(), other.sub())
|
||||
}
|
||||
@ -93,7 +93,7 @@ impl Collider for Route {
|
||||
&& self.rank == b.rank
|
||||
&& self.path.collides_with(&b.path)
|
||||
&& match (self.format.as_ref(), b.format.as_ref()) {
|
||||
(Some(ct_a), Some(ct_b)) => ct_a.collides_with(ct_b),
|
||||
(Some(mt_a), Some(mt_b)) => mt_a.collides_with(mt_b),
|
||||
(Some(_), None) => true,
|
||||
(None, Some(_)) => true,
|
||||
(None, None) => true
|
||||
@ -115,7 +115,7 @@ impl<'r> Collider<Request<'r>> for Route {
|
||||
&& self.path.query().map_or(true, |_| req.uri().query().is_some())
|
||||
// FIXME: On payload requests, check Content-Type, else Accept.
|
||||
&& match (req.content_type().as_ref(), self.format.as_ref()) {
|
||||
(Some(ct_a), Some(ct_b)) => ct_a.collides_with(ct_b),
|
||||
(Some(mt_a), Some(mt_b)) => mt_a.collides_with(mt_b),
|
||||
(Some(_), None) => true,
|
||||
(None, Some(_)) => false,
|
||||
(None, None) => true
|
||||
@ -132,7 +132,7 @@ mod tests {
|
||||
use data::Data;
|
||||
use handler::Outcome;
|
||||
use router::route::Route;
|
||||
use http::{Method, ContentType};
|
||||
use http::{Method, MediaType, ContentType};
|
||||
use http::uri::URI;
|
||||
use http::Method::*;
|
||||
|
||||
@ -301,41 +301,41 @@ mod tests {
|
||||
assert!(!s_s_collide("/a/hi/<a..>", "/a/hi/"));
|
||||
}
|
||||
|
||||
fn ct_ct_collide(ct1: &str, ct2: &str) -> bool {
|
||||
let ct_a = ContentType::from_str(ct1).expect(ct1);
|
||||
let ct_b = ContentType::from_str(ct2).expect(ct2);
|
||||
ct_a.collides_with(&ct_b)
|
||||
fn mt_mt_collide(mt1: &str, mt2: &str) -> bool {
|
||||
let mt_a = MediaType::from_str(mt1).expect(mt1);
|
||||
let mt_b = MediaType::from_str(mt2).expect(mt2);
|
||||
mt_a.collides_with(&mt_b)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_content_type_colliions() {
|
||||
assert!(ct_ct_collide("application/json", "application/json"));
|
||||
assert!(ct_ct_collide("*/json", "application/json"));
|
||||
assert!(ct_ct_collide("*/*", "application/json"));
|
||||
assert!(ct_ct_collide("application/*", "application/json"));
|
||||
assert!(ct_ct_collide("application/*", "*/json"));
|
||||
assert!(ct_ct_collide("something/random", "something/random"));
|
||||
assert!(mt_mt_collide("application/json", "application/json"));
|
||||
assert!(mt_mt_collide("*/json", "application/json"));
|
||||
assert!(mt_mt_collide("*/*", "application/json"));
|
||||
assert!(mt_mt_collide("application/*", "application/json"));
|
||||
assert!(mt_mt_collide("application/*", "*/json"));
|
||||
assert!(mt_mt_collide("something/random", "something/random"));
|
||||
|
||||
assert!(!ct_ct_collide("text/*", "application/*"));
|
||||
assert!(!ct_ct_collide("*/text", "*/json"));
|
||||
assert!(!ct_ct_collide("*/text", "application/test"));
|
||||
assert!(!ct_ct_collide("something/random", "something_else/random"));
|
||||
assert!(!ct_ct_collide("something/random", "*/else"));
|
||||
assert!(!ct_ct_collide("*/random", "*/else"));
|
||||
assert!(!ct_ct_collide("something/*", "random/else"));
|
||||
assert!(!mt_mt_collide("text/*", "application/*"));
|
||||
assert!(!mt_mt_collide("*/text", "*/json"));
|
||||
assert!(!mt_mt_collide("*/text", "application/test"));
|
||||
assert!(!mt_mt_collide("something/random", "something_else/random"));
|
||||
assert!(!mt_mt_collide("something/random", "*/else"));
|
||||
assert!(!mt_mt_collide("*/random", "*/else"));
|
||||
assert!(!mt_mt_collide("something/*", "random/else"));
|
||||
}
|
||||
|
||||
fn r_ct_ct_collide<S1, S2>(m1: Method, ct1: S1, m2: Method, ct2: S2) -> bool
|
||||
fn r_mt_mt_collide<S1, S2>(m1: Method, mt1: S1, m2: Method, mt2: S2) -> bool
|
||||
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
|
||||
{
|
||||
let mut route_a = Route::new(m1, "/", dummy_handler);
|
||||
if let Some(ct_str) = ct1.into() {
|
||||
route_a.format = Some(ct_str.parse::<ContentType>().unwrap());
|
||||
if let Some(mt_str) = mt1.into() {
|
||||
route_a.format = Some(mt_str.parse::<MediaType>().unwrap());
|
||||
}
|
||||
|
||||
let mut route_b = Route::new(m2, "/", dummy_handler);
|
||||
if let Some(ct_str) = ct2.into() {
|
||||
route_b.format = Some(ct_str.parse::<ContentType>().unwrap());
|
||||
if let Some(mt_str) = mt2.into() {
|
||||
route_b.format = Some(mt_str.parse::<MediaType>().unwrap());
|
||||
}
|
||||
|
||||
route_a.collides_with(&route_b)
|
||||
@ -343,61 +343,61 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_route_content_type_colliions() {
|
||||
assert!(r_ct_ct_collide(Get, "application/json", Get, "application/json"));
|
||||
assert!(r_ct_ct_collide(Get, "*/json", Get, "application/json"));
|
||||
assert!(r_ct_ct_collide(Get, "*/json", Get, "application/*"));
|
||||
assert!(r_ct_ct_collide(Get, "text/html", Get, "text/*"));
|
||||
assert!(r_ct_ct_collide(Get, "any/thing", Get, "*/*"));
|
||||
assert!(r_mt_mt_collide(Get, "application/json", Get, "application/json"));
|
||||
assert!(r_mt_mt_collide(Get, "*/json", Get, "application/json"));
|
||||
assert!(r_mt_mt_collide(Get, "*/json", Get, "application/*"));
|
||||
assert!(r_mt_mt_collide(Get, "text/html", Get, "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, "any/thing", Get, "*/*"));
|
||||
|
||||
assert!(r_ct_ct_collide(Get, None, Get, "text/*"));
|
||||
assert!(r_ct_ct_collide(Get, None, Get, "text/html"));
|
||||
assert!(r_ct_ct_collide(Get, None, Get, "*/*"));
|
||||
assert!(r_ct_ct_collide(Get, "text/html", Get, None));
|
||||
assert!(r_ct_ct_collide(Get, "*/*", Get, None));
|
||||
assert!(r_ct_ct_collide(Get, "application/json", Get, None));
|
||||
assert!(r_mt_mt_collide(Get, None, Get, "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, None, Get, "text/html"));
|
||||
assert!(r_mt_mt_collide(Get, None, Get, "*/*"));
|
||||
assert!(r_mt_mt_collide(Get, "text/html", Get, None));
|
||||
assert!(r_mt_mt_collide(Get, "*/*", Get, None));
|
||||
assert!(r_mt_mt_collide(Get, "application/json", Get, None));
|
||||
|
||||
assert!(!r_ct_ct_collide(Get, "text/html", Get, "application/*"));
|
||||
assert!(!r_ct_ct_collide(Get, "application/html", Get, "text/*"));
|
||||
assert!(!r_ct_ct_collide(Get, "*/json", Get, "text/html"));
|
||||
assert!(!r_ct_ct_collide(Get, "text/html", Get, "text/css"));
|
||||
assert!(!r_mt_mt_collide(Get, "text/html", Get, "application/*"));
|
||||
assert!(!r_mt_mt_collide(Get, "application/html", Get, "text/*"));
|
||||
assert!(!r_mt_mt_collide(Get, "*/json", Get, "text/html"));
|
||||
assert!(!r_mt_mt_collide(Get, "text/html", Get, "text/css"));
|
||||
}
|
||||
|
||||
fn req_route_ct_collide<S1, S2>(m1: Method, ct1: S1, m2: Method, ct2: S2) -> bool
|
||||
fn req_route_mt_collide<S1, S2>(m1: Method, mt1: S1, m2: Method, mt2: S2) -> bool
|
||||
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
|
||||
{
|
||||
let mut req = Request::new(m1, "/");
|
||||
if let Some(ct_str) = ct1.into() {
|
||||
req.replace_header(ct_str.parse::<ContentType>().unwrap());
|
||||
if let Some(mt_str) = mt1.into() {
|
||||
req.replace_header(mt_str.parse::<ContentType>().unwrap());
|
||||
}
|
||||
|
||||
let mut route = Route::new(m2, "/", dummy_handler);
|
||||
if let Some(ct_str) = ct2.into() {
|
||||
route.format = Some(ct_str.parse::<ContentType>().unwrap());
|
||||
if let Some(mt_str) = mt2.into() {
|
||||
route.format = Some(mt_str.parse::<MediaType>().unwrap());
|
||||
}
|
||||
|
||||
route.collides_with(&req)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_req_route_ct_collisions() {
|
||||
assert!(req_route_ct_collide(Get, "application/json", Get, "application/json"));
|
||||
assert!(req_route_ct_collide(Get, "application/json", Get, "application/*"));
|
||||
assert!(req_route_ct_collide(Get, "application/json", Get, "*/json"));
|
||||
assert!(req_route_ct_collide(Get, "text/html", Get, "text/html"));
|
||||
assert!(req_route_ct_collide(Get, "text/html", Get, "*/*"));
|
||||
fn test_req_route_mt_collisions() {
|
||||
assert!(req_route_mt_collide(Get, "application/json", Get, "application/json"));
|
||||
assert!(req_route_mt_collide(Get, "application/json", Get, "application/*"));
|
||||
assert!(req_route_mt_collide(Get, "application/json", Get, "*/json"));
|
||||
assert!(req_route_mt_collide(Get, "text/html", Get, "text/html"));
|
||||
assert!(req_route_mt_collide(Get, "text/html", Get, "*/*"));
|
||||
|
||||
assert!(req_route_ct_collide(Get, "text/html", Get, None));
|
||||
assert!(req_route_ct_collide(Get, None, Get, None));
|
||||
assert!(req_route_ct_collide(Get, "application/json", Get, None));
|
||||
assert!(req_route_ct_collide(Get, "x-custom/anything", Get, None));
|
||||
assert!(req_route_mt_collide(Get, "text/html", Get, None));
|
||||
assert!(req_route_mt_collide(Get, None, Get, None));
|
||||
assert!(req_route_mt_collide(Get, "application/json", Get, None));
|
||||
assert!(req_route_mt_collide(Get, "x-custom/anything", Get, None));
|
||||
|
||||
assert!(!req_route_ct_collide(Get, "application/json", Get, "text/html"));
|
||||
assert!(!req_route_ct_collide(Get, "application/json", Get, "text/*"));
|
||||
assert!(!req_route_ct_collide(Get, "application/json", Get, "*/xml"));
|
||||
assert!(!req_route_mt_collide(Get, "application/json", Get, "text/html"));
|
||||
assert!(!req_route_mt_collide(Get, "application/json", Get, "text/*"));
|
||||
assert!(!req_route_mt_collide(Get, "application/json", Get, "*/xml"));
|
||||
|
||||
assert!(!req_route_ct_collide(Get, None, Get, "text/html"));
|
||||
assert!(!req_route_ct_collide(Get, None, Get, "*/*"));
|
||||
assert!(!req_route_ct_collide(Get, None, Get, "application/json"));
|
||||
assert!(!req_route_mt_collide(Get, None, Get, "text/html"));
|
||||
assert!(!req_route_mt_collide(Get, None, Get, "*/*"));
|
||||
assert!(!req_route_mt_collide(Get, None, Get, "application/json"));
|
||||
}
|
||||
|
||||
fn req_route_path_collide(a: &'static str, b: &'static str) -> bool {
|
||||
|
@ -6,10 +6,10 @@ use term_painter::Color::*;
|
||||
|
||||
use codegen::StaticRouteInfo;
|
||||
use handler::Handler;
|
||||
use http::{Method, ContentType};
|
||||
use http::{Method, MediaType};
|
||||
use http::uri::URI;
|
||||
|
||||
/// A route: a method, its handler, path, rank, and format/content type.
|
||||
/// A route: a method, its handler, path, rank, and format/media type.
|
||||
pub struct Route {
|
||||
/// The method this route matches against.
|
||||
pub method: Method,
|
||||
@ -19,8 +19,8 @@ pub struct Route {
|
||||
pub path: URI<'static>,
|
||||
/// The rank of this route. Lower ranks have higher priorities.
|
||||
pub rank: isize,
|
||||
/// The Content-Type this route matches against.
|
||||
pub format: Option<ContentType>,
|
||||
/// The media type this route matches against.
|
||||
pub format: Option<MediaType>,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
Loading…
Reference in New Issue
Block a user