diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml index 9c83d37e..18327844 100644 --- a/core/codegen/Cargo.toml +++ b/core/codegen/Cargo.toml @@ -26,7 +26,7 @@ glob = "0.3" [dev-dependencies] rocket = { version = "0.5.0-rc.1", path = "../lib", features = ["json", "msgpack"] } +time = { version = "0.3", features = ["macros"] } pretty_assertions = "0.7" version_check = "0.9" trybuild = "1.0" -time = "0.2.11" diff --git a/core/codegen/tests/from_form.rs b/core/codegen/tests/from_form.rs index 19a70362..fdd72dd5 100644 --- a/core/codegen/tests/from_form.rs +++ b/core/codegen/tests/from_form.rs @@ -1,5 +1,6 @@ use std::net::{IpAddr, SocketAddr}; use std::collections::{BTreeMap, HashMap}; + use pretty_assertions::assert_eq; use rocket::UriDisplayQuery; @@ -732,13 +733,13 @@ fn test_defaults() { ip: IpAddr, #[field(default = ([192u8, 168, 1, 0], 20))] addr: SocketAddr, - #[field(default = time::date!(2021-05-27))] + #[field(default = time::macros::date!(2021-05-27))] date: time::Date, - #[field(default = time::time!(01:15:00))] + #[field(default = time::macros::time!(01:15:00))] time: time::Time, #[field(default = time::PrimitiveDateTime::new( - time::date!(2021-05-27), - time::time!(01:15:00), + time::macros::date!(2021-05-27), + time::macros::time!(01:15:00), ))] datetime: time::PrimitiveDateTime, } @@ -773,9 +774,12 @@ fn test_defaults() { string: "wowie".to_string(), ip: [192u8, 168, 1, 0].into(), addr: ([192u8, 168, 1, 0], 20).into(), - date: time::date!(2021-05-27), - time: time::time!(01:15:00), - datetime: time::PrimitiveDateTime::new(time::date!(2021-05-27), time::time!(01:15:00)), + date: time::macros::date!(2021-05-27), + time: time::macros::time!(01:15:00), + datetime: time::PrimitiveDateTime::new( + time::macros::date!(2021-05-27), + time::macros::time!(01:15:00) + ), })); let form2: Option = strict(&form_string).ok(); diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index 96462b95..f8bdc165 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -26,7 +26,7 @@ uuid = ["uuid_"] smallvec = "1.0" percent-encoding = "2" http = "0.2" -time = "0.2.11" +time = { version = "0.3", features = ["formatting", "macros"] } indexmap = { version = "1.5.2", features = ["std"] } rustls = { version = "0.19", optional = true } tokio-rustls = { version = "0.22.0", optional = true } @@ -39,7 +39,7 @@ pear = "0.2.3" pin-project-lite = "0.2" memchr = "2" stable-pattern = "0.1" -cookie = { version = "0.15", features = ["percent-encode"] } +cookie = { version = "0.16.0-rc.1", features = ["percent-encode"] } state = "0.5.1" [dependencies.x509-parser] diff --git a/core/http/src/uri/fmt/uri_display.rs b/core/http/src/uri/fmt/uri_display.rs index 50ecf187..fcaebf02 100644 --- a/core/http/src/uri/fmt/uri_display.rs +++ b/core/http/src/uri/fmt/uri_display.rs @@ -2,6 +2,8 @@ use std::collections::{BTreeMap, HashMap}; use std::{fmt, path}; use std::borrow::Cow; +use time::{macros::format_description, format_description::FormatItem}; + use crate::RawStr; use crate::uri::fmt::{Part, Path, Query, Formatter}; @@ -354,13 +356,13 @@ impl_with_display! { macro_rules! impl_with_string { ($($T:ty => $f:expr),+ $(,)?) => {$( - /// This implementation is identical to a percent-encoded versiono the + /// This implementation is identical to a percent-encoded version of the /// `Display` implementation. impl UriDisplay

for $T { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result { - let func: fn(&$T) -> String = $f; - func(self).as_str().fmt(f) + let func: fn(&$T) -> Result = $f; + func(self).and_then(|s| s.as_str().fmt(f)) } } )+} @@ -368,14 +370,20 @@ macro_rules! impl_with_string { use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; -// Keep in-sync with the 'FromUriParam' impls. +// Keep formats in sync with 'FromFormField' impls. +static DATE_FMT: &[FormatItem<'_>] = format_description!("[year padding:none]-[month]-[day]"); +static TIME_FMT: &[FormatItem<'_>] = format_description!("[hour padding:none]:[minute]:[second]"); +static DATE_TIME_FMT: &[FormatItem<'_>] = + format_description!("[year padding:none]-[month]-[day]T[hour padding:none]:[minute]:[second]"); + +// Keep list in sync with the 'FromUriParam' impls. impl_with_string! { - time::Date => |d| d.format("%F"), - time::PrimitiveDateTime => |d| d.format("%FT%T"), - time::Time => |d| d.format("%T"), - SocketAddr => |s| s.to_string(), - SocketAddrV4 => |s| s.to_string(), - SocketAddrV6 => |s| s.to_string(), + time::Date => |d| d.format(&DATE_FMT).map_err(|_| fmt::Error), + time::Time => |d| d.format(&TIME_FMT).map_err(|_| fmt::Error), + time::PrimitiveDateTime => |d| d.format(&DATE_TIME_FMT).map_err(|_| fmt::Error), + SocketAddr => |a| Ok(a.to_string()), + SocketAddrV4 => |a| Ok(a.to_string()), + SocketAddrV6 => |a| Ok(a.to_string()), } // These are second level implementations: they all defer to an existing diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index c9d1f334..758fb4ea 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -38,7 +38,7 @@ futures = { version = "0.3.0", default-features = false, features = ["std"] } yansi = "0.5" log = { version = "0.4", features = ["std"] } num_cpus = "1.0" -time = "0.2.11" +time = { version = "0.3", features = ["macros", "parsing"] } memchr = "2" # TODO: Use pear instead. binascii = "0.1" atty = "0.2" diff --git a/core/lib/src/form/from_form_field.rs b/core/lib/src/form/from_form_field.rs index b866300f..69b87f31 100644 --- a/core/lib/src/form/from_form_field.rs +++ b/core/lib/src/form/from_form_field.rs @@ -6,6 +6,7 @@ use std::num::{ }; use time::{Date, Time, PrimitiveDateTime}; +use time::{macros::format_description, format_description::FormatItem}; use crate::data::Capped; use crate::http::uncased::AsUncased; @@ -355,10 +356,19 @@ impl_with_parse!( NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, Ipv4Addr, IpAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr ); +// +// Keep formats in sync with 'FromFormField' impls. +static DATE_FMT: &[FormatItem<'_>] = format_description!("[year padding:none]-[month]-[day]"); +static TIME_FMT1: &[FormatItem<'_>] = format_description!("[hour padding:none]:[minute]:[second]"); +static TIME_FMT2: &[FormatItem<'_>] = format_description!("[hour padding:none]:[minute]"); +static DATE_TIME_FMT1: &[FormatItem<'_>] = + format_description!("[year padding:none]-[month]-[day]T[hour padding:none]:[minute]:[second]"); +static DATE_TIME_FMT2: &[FormatItem<'_>] = + format_description!("[year padding:none]-[month]-[day]T[hour padding:none]:[minute]"); impl<'v> FromFormField<'v> for Date { fn from_value(field: ValueField<'v>) -> Result<'v, Self> { - let date = Self::parse(field.value, "%F") + let date = Self::parse(field.value, &DATE_FMT) .map_err(|e| Box::new(e) as Box)?; Ok(date) @@ -367,8 +377,8 @@ impl<'v> FromFormField<'v> for Date { impl<'v> FromFormField<'v> for Time { fn from_value(field: ValueField<'v>) -> Result<'v, Self> { - let time = Self::parse(field.value, "%T") - .or_else(|_| Self::parse(field.value, "%R")) + let time = Self::parse(field.value, &TIME_FMT1) + .or_else(|_| Self::parse(field.value, &TIME_FMT2)) .map_err(|e| Box::new(e) as Box)?; Ok(time) @@ -377,8 +387,8 @@ impl<'v> FromFormField<'v> for Time { impl<'v> FromFormField<'v> for PrimitiveDateTime { fn from_value(field: ValueField<'v>) -> Result<'v, Self> { - let dt = Self::parse(field.value, "%FT%T") - .or_else(|_| Self::parse(field.value, "%FT%R")) + let dt = Self::parse(field.value, &DATE_TIME_FMT1) + .or_else(|_| Self::parse(field.value, &DATE_TIME_FMT2)) .map_err(|e| Box::new(e) as Box)?; Ok(dt) diff --git a/core/lib/src/form/tests.rs b/core/lib/src/form/tests.rs index 9bbe9d10..0165318a 100644 --- a/core/lib/src/form/tests.rs +++ b/core/lib/src/form/tests.rs @@ -64,7 +64,7 @@ macro_rules! assert_parse_fails { #[test] fn time() { - use time::{date, time, Date, Time, PrimitiveDateTime as DateTime}; + use time::{macros::{date, time}, Date, Time, PrimitiveDateTime as DateTime}; assert_values_parse_eq! { &["=2010-10-20"] => Date = date!(2010-10-20), diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 3fe85b2c..1479a40f 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -106,6 +106,7 @@ pub use futures; pub use tokio; pub use figment; +pub use time; #[doc(hidden)] #[macro_use] pub mod log; diff --git a/examples/forms/Cargo.toml b/examples/forms/Cargo.toml index 338aef04..6f431e91 100644 --- a/examples/forms/Cargo.toml +++ b/examples/forms/Cargo.toml @@ -7,7 +7,6 @@ publish = false [dependencies] rocket = { path = "../../core/lib" } -time = "0.2" [dependencies.rocket_dyn_templates] path = "../../contrib/dyn_templates" diff --git a/examples/forms/src/main.rs b/examples/forms/src/main.rs index 7737bdc6..9dc93bfe 100644 --- a/examples/forms/src/main.rs +++ b/examples/forms/src/main.rs @@ -1,9 +1,9 @@ #[macro_use] extern crate rocket; +use rocket::time::Date; use rocket::http::{Status, ContentType}; use rocket::form::{Form, Contextual, FromForm, FromFormField, Context}; -use rocket::fs::TempFile; -use rocket::fs::{FileServer, relative}; +use rocket::fs::{FileServer, TempFile, relative}; use rocket_dyn_templates::Template; @@ -36,7 +36,7 @@ enum Category { struct Submission<'v> { #[field(validate = len(1..))] title: &'v str, - date: time::Date, + date: Date, #[field(validate = len(1..=250))] r#abstract: &'v str, #[field(validate = ext(ContentType::PDF))]