mirror of https://github.com/rwf2/Rocket.git
Graduate contrib 'uuid' into core.
This has the following nice benefits: * The 'Uuid' wrapper type is gone. * 'Uuid' implements 'UriDisplay', 'FromUriParam'. * The 'serialization' example merges in 'uuid'. Resolves #1299.
This commit is contained in:
parent
f6a7087c84
commit
a78814f1c5
|
@ -27,7 +27,6 @@ serve = []
|
||||||
compression = ["brotli_compression", "gzip_compression"]
|
compression = ["brotli_compression", "gzip_compression"]
|
||||||
brotli_compression = ["brotli"]
|
brotli_compression = ["brotli"]
|
||||||
gzip_compression = ["flate2"]
|
gzip_compression = ["flate2"]
|
||||||
uuid = ["serde", "_uuid"]
|
|
||||||
|
|
||||||
# The barage of user-facing database features.
|
# The barage of user-facing database features.
|
||||||
diesel_sqlite_pool = ["databases", "diesel/sqlite", "diesel/r2d2"]
|
diesel_sqlite_pool = ["databases", "diesel/sqlite", "diesel/r2d2"]
|
||||||
|
@ -69,12 +68,6 @@ r2d2-memcache = { version = "0.6", optional = true }
|
||||||
brotli = { version = "3.3", optional = true }
|
brotli = { version = "3.3", optional = true }
|
||||||
flate2 = { version = "1.0", optional = true }
|
flate2 = { version = "1.0", optional = true }
|
||||||
|
|
||||||
[dependencies._uuid]
|
|
||||||
package = "uuid"
|
|
||||||
version = ">=0.7.0, <0.9.0"
|
|
||||||
optional = true
|
|
||||||
features = ["serde"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_test = "1.0.114"
|
serde_test = "1.0.114"
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
//! * [serve*](serve) - Static File Serving
|
//! * [serve*](serve) - Static File Serving
|
||||||
//! * [handlebars_templates](templates) - Handlebars Templating
|
//! * [handlebars_templates](templates) - Handlebars Templating
|
||||||
//! * [tera_templates](templates) - Tera Templating
|
//! * [tera_templates](templates) - Tera Templating
|
||||||
//! * [uuid](uuid) - UUID (de)serialization
|
|
||||||
//! * [${database}_pool](databases) - Database Configuration and Pooling
|
//! * [${database}_pool](databases) - Database Configuration and Pooling
|
||||||
//!
|
//!
|
||||||
//! The recommend way to include features from this crate via Rocket in your
|
//! The recommend way to include features from this crate via Rocket in your
|
||||||
|
@ -42,7 +41,6 @@
|
||||||
|
|
||||||
#[cfg(feature="serve")] pub mod serve;
|
#[cfg(feature="serve")] pub mod serve;
|
||||||
#[cfg(feature="templates")] pub mod templates;
|
#[cfg(feature="templates")] pub mod templates;
|
||||||
#[cfg(feature="uuid")] pub mod uuid;
|
|
||||||
#[cfg(feature="databases")] pub mod databases;
|
#[cfg(feature="databases")] pub mod databases;
|
||||||
// TODO.async: Migrate compression, reenable this, tests, and add to docs.
|
// TODO.async: Migrate compression, reenable this, tests, and add to docs.
|
||||||
//#[cfg(any(feature="brotli_compression", feature="gzip_compression"))] pub mod compression;
|
//#[cfg(any(feature="brotli_compression", feature="gzip_compression"))] pub mod compression;
|
||||||
|
|
|
@ -1,203 +0,0 @@
|
||||||
//! UUID parameter and form value parsing support.
|
|
||||||
//!
|
|
||||||
//! See the [`Uuid`] type for further details.
|
|
||||||
//!
|
|
||||||
//! # Enabling
|
|
||||||
//!
|
|
||||||
//! This module is only available when the `uuid` feature is enabled. Enable it
|
|
||||||
//! in `Cargo.toml` as follows:
|
|
||||||
//!
|
|
||||||
//! ```toml
|
|
||||||
//! [dependencies.rocket_contrib]
|
|
||||||
//! version = "0.5.0-dev"
|
|
||||||
//! default-features = false
|
|
||||||
//! features = ["uuid"]
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
pub extern crate _uuid as extern_uuid;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use rocket::request::FromParam;
|
|
||||||
use rocket::form::{self, FromFormField, ValueField};
|
|
||||||
|
|
||||||
/// UUID data and form guard: consume UUID values.
|
|
||||||
///
|
|
||||||
/// `Uuid` implements [`FromParam`] and [`FromFormField`], allowing UUID values
|
|
||||||
/// to be accepted directly in paths, queries, and forms.
|
|
||||||
///
|
|
||||||
/// # Usage
|
|
||||||
///
|
|
||||||
/// To use, add the `uuid` feature to the `rocket_contrib` dependencies section
|
|
||||||
/// of your `Cargo.toml`:
|
|
||||||
///
|
|
||||||
/// ```toml
|
|
||||||
/// [dependencies.rocket_contrib]
|
|
||||||
/// version = "0.5.0-dev"
|
|
||||||
/// default-features = false
|
|
||||||
/// features = ["uuid"]
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// You can use the `Uuid` type directly as a target of a dynamic parameter:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # #[macro_use] extern crate rocket;
|
|
||||||
/// # #[macro_use] extern crate rocket_contrib;
|
|
||||||
/// use rocket_contrib::uuid::Uuid;
|
|
||||||
///
|
|
||||||
/// #[get("/users/<id>")]
|
|
||||||
/// fn user(id: Uuid) -> String {
|
|
||||||
/// format!("We found: {}", id)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// You can also use the `Uuid` as a form value, including in query strings:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # #[macro_use] extern crate rocket;
|
|
||||||
/// # #[macro_use] extern crate rocket_contrib;
|
|
||||||
/// use rocket_contrib::uuid::Uuid;
|
|
||||||
///
|
|
||||||
/// #[get("/user?<id>")]
|
|
||||||
/// fn user(id: Uuid) -> String {
|
|
||||||
/// format!("User ID: {}", id)
|
|
||||||
/// }
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(transparent)]
|
|
||||||
pub struct Uuid(_uuid::Uuid);
|
|
||||||
|
|
||||||
pub type Error = <_uuid::Uuid as std::str::FromStr>::Err;
|
|
||||||
|
|
||||||
impl Uuid {
|
|
||||||
/// Consumes the Uuid wrapper, returning the underlying `Uuid` type.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// # use std::str::FromStr;
|
|
||||||
/// # fn main() {
|
|
||||||
/// use rocket_contrib::uuid::{extern_uuid, Uuid};
|
|
||||||
///
|
|
||||||
/// let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2";
|
|
||||||
/// let real_uuid = extern_uuid::Uuid::from_str(uuid_str).unwrap();
|
|
||||||
/// let my_inner_uuid = Uuid::from_str(uuid_str)
|
|
||||||
/// .expect("valid UUID string")
|
|
||||||
/// .into_inner();
|
|
||||||
///
|
|
||||||
/// assert_eq!(real_uuid, my_inner_uuid);
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn into_inner(self) -> _uuid::Uuid {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Uuid {
|
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromParam<'a> for Uuid {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
/// A value is successfully parsed if `param` is a properly formatted Uuid.
|
|
||||||
/// Otherwise, an error is returned.
|
|
||||||
#[inline(always)]
|
|
||||||
fn from_param(param: &'a str) -> Result<Uuid, Self::Error> {
|
|
||||||
param.parse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'v> FromFormField<'v> for Uuid {
|
|
||||||
fn from_value(field: ValueField<'v>) -> form::Result<'v, Self> {
|
|
||||||
Ok(field.value.parse().map_err(form::error::Error::custom)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Uuid {
|
|
||||||
type Err = Error;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_str(s: &str) -> Result<Uuid, Self::Err> {
|
|
||||||
s.parse().map(Uuid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Uuid {
|
|
||||||
type Target = _uuid::Uuid;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<_uuid::Uuid> for Uuid {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &_uuid::Uuid) -> bool {
|
|
||||||
self.0.eq(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::Uuid;
|
|
||||||
use super::FromParam;
|
|
||||||
use super::FromStr;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_str() {
|
|
||||||
let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2";
|
|
||||||
let uuid_wrapper = Uuid::from_str(uuid_str).unwrap();
|
|
||||||
assert_eq!(uuid_str, uuid_wrapper.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_param() {
|
|
||||||
let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2";
|
|
||||||
let uuid_wrapper = Uuid::from_param(uuid_str.into()).unwrap();
|
|
||||||
assert_eq!(uuid_str, uuid_wrapper.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_into_inner() {
|
|
||||||
let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2";
|
|
||||||
let uuid_wrapper = Uuid::from_param(uuid_str.into()).unwrap();
|
|
||||||
let real_uuid: _uuid::Uuid = uuid_str.parse().unwrap();
|
|
||||||
let inner_uuid: _uuid::Uuid = uuid_wrapper.into_inner();
|
|
||||||
assert_eq!(real_uuid, inner_uuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_partial_eq() {
|
|
||||||
let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2";
|
|
||||||
let uuid_wrapper = Uuid::from_param(uuid_str.into()).unwrap();
|
|
||||||
let real_uuid: _uuid::Uuid = uuid_str.parse().unwrap();
|
|
||||||
assert_eq!(uuid_wrapper, real_uuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "InvalidLength")]
|
|
||||||
fn test_from_param_invalid() {
|
|
||||||
let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2p";
|
|
||||||
Uuid::from_param(uuid_str.into()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ser_de() {
|
|
||||||
// The main reason for this test is to test that UUID only serializes as
|
|
||||||
// a string token, not anything else. Like a Struct with a named field...
|
|
||||||
use serde_test::{Token, assert_tokens, Configure};
|
|
||||||
let uuid: Uuid = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2".parse().unwrap();
|
|
||||||
|
|
||||||
assert_tokens(&uuid.readable(), &[
|
|
||||||
Token::Str("c1aa1e3b-9614-4895-9ebd-705255fa5bc2"),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ default = []
|
||||||
tls = ["tokio-rustls"]
|
tls = ["tokio-rustls"]
|
||||||
private-cookies = ["cookie/private", "cookie/key-expansion"]
|
private-cookies = ["cookie/private", "cookie/key-expansion"]
|
||||||
serde = ["uncased/with-serde-alloc", "_serde"]
|
serde = ["uncased/with-serde-alloc", "_serde"]
|
||||||
|
uuid = ["_uuid"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smallvec = "1.0"
|
smallvec = "1.0"
|
||||||
|
@ -56,5 +57,11 @@ optional = true
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["std"]
|
features = ["std"]
|
||||||
|
|
||||||
|
[dependencies._uuid]
|
||||||
|
package = "uuid"
|
||||||
|
version = "0.8"
|
||||||
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rocket = { version = "0.5.0-dev", path = "../lib" }
|
rocket = { version = "0.5.0-dev", path = "../lib" }
|
||||||
|
|
|
@ -259,12 +259,13 @@ use crate::uri::fmt::{Part, Path, Query, Formatter};
|
||||||
/// use rocket::response::Redirect;
|
/// use rocket::response::Redirect;
|
||||||
///
|
///
|
||||||
/// impl UriDisplay<Path> for Name<'_> {
|
/// impl UriDisplay<Path> for Name<'_> {
|
||||||
/// // Delegates to the `UriDisplay` implementation for `str` via the call
|
/// // Writes the raw string `name:`, which is URI-safe, and then delegates
|
||||||
/// // to `write_value` to ensure that the written string is URI-safe. In
|
/// // to the `UriDisplay` implementation for `str` which ensures that
|
||||||
/// // this case, the string will be percent encoded. Prefixes the inner
|
/// // string is written in a URI-safe manner. In this case, the string will
|
||||||
/// // name with `name:`.
|
/// // be percent encoded.
|
||||||
/// fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
|
/// fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
|
||||||
/// f.write_value(&format!("name:{}", self.0))
|
/// f.write_raw("name:")?;
|
||||||
|
/// UriDisplay::fmt(&self.0, f)
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
@ -408,6 +409,9 @@ impl<T: UriDisplay<Query>, E> UriDisplay<Query> for Result<T, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "uuid")] impl_with_display!(_uuid::Uuid);
|
||||||
|
#[cfg(feature = "uuid")] crate::impl_from_uri_param_identity!(_uuid::Uuid);
|
||||||
|
|
||||||
// And finally, the `Ignorable` trait, which has sugar of `_` in the `uri!`
|
// And finally, the `Ignorable` trait, which has sugar of `_` in the `uri!`
|
||||||
// macro, which expands to a typecheck.
|
// macro, which expands to a typecheck.
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,13 @@ tls = ["rocket_http/tls"]
|
||||||
secrets = ["rocket_http/private-cookies"]
|
secrets = ["rocket_http/private-cookies"]
|
||||||
json = ["serde_json", "tokio/io-util"]
|
json = ["serde_json", "tokio/io-util"]
|
||||||
msgpack = ["rmp-serde", "tokio/io-util"]
|
msgpack = ["rmp-serde", "tokio/io-util"]
|
||||||
|
uuid = ["_uuid", "rocket_http/uuid"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Serialization dependencies.
|
# Serialization dependencies.
|
||||||
serde_json = { version = "1.0.26", optional = true }
|
serde_json = { version = "1.0.26", optional = true }
|
||||||
rmp-serde = { version = "0.15.0", optional = true }
|
rmp-serde = { version = "0.15.0", optional = true }
|
||||||
|
_uuid = { package = "uuid", version = "0.8", optional = true, features = ["serde"] }
|
||||||
|
|
||||||
# Non-optional, core dependencies from here on out.
|
# Non-optional, core dependencies from here on out.
|
||||||
futures = "0.3.0"
|
futures = "0.3.0"
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
//! | `tls` | Support for [TLS] encrypted connections. |
|
//! | `tls` | Support for [TLS] encrypted connections. |
|
||||||
//! | `json` | Support for [JSON (de)serialization]. |
|
//! | `json` | Support for [JSON (de)serialization]. |
|
||||||
//! | `msgpack` | Support for [MessagePack (de)serialization]. |
|
//! | `msgpack` | Support for [MessagePack (de)serialization]. |
|
||||||
|
//! | `uuid` | Support for [UUID value parsing and (de)serialization]. |
|
||||||
//!
|
//!
|
||||||
//! Features can be selectively enabled in `Cargo.toml`:
|
//! Features can be selectively enabled in `Cargo.toml`:
|
||||||
//!
|
//!
|
||||||
|
@ -74,6 +75,7 @@
|
||||||
//!
|
//!
|
||||||
//! [JSON (de)serialization]: crate::serde::json
|
//! [JSON (de)serialization]: crate::serde::json
|
||||||
//! [MessagePack (de)serialization]: crate::serde::msgpack
|
//! [MessagePack (de)serialization]: crate::serde::msgpack
|
||||||
|
//! [UUID value parsing and (de)serialization]: crate::serde::uuid
|
||||||
//! [private cookies]: https://rocket.rs/master/guide/requests/#private-cookies
|
//! [private cookies]: https://rocket.rs/master/guide/requests/#private-cookies
|
||||||
//! [TLS]: https://rocket.rs/master/guide/configuration/#tls
|
//! [TLS]: https://rocket.rs/master/guide/configuration/#tls
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -16,3 +16,7 @@ pub mod json;
|
||||||
#[cfg(feature = "msgpack")]
|
#[cfg(feature = "msgpack")]
|
||||||
#[cfg_attr(nightly, doc(cfg(feature = "msgpack")))]
|
#[cfg_attr(nightly, doc(cfg(feature = "msgpack")))]
|
||||||
pub mod msgpack;
|
pub mod msgpack;
|
||||||
|
|
||||||
|
#[cfg(feature = "uuid")]
|
||||||
|
#[cfg_attr(nightly, doc(cfg(feature = "uuid")))]
|
||||||
|
pub mod uuid;
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
//! UUID path/query parameter and form value parsing support.
|
||||||
|
//!
|
||||||
|
//! # Enabling
|
||||||
|
//!
|
||||||
|
//! This module is only available when the `uuid` feature is enabled. Enable it
|
||||||
|
//! in `Cargo.toml` as follows:
|
||||||
|
//!
|
||||||
|
//! ```toml
|
||||||
|
//! [dependencies.rocket]
|
||||||
|
//! version = "0.5.0-dev"
|
||||||
|
//! features = ["uuid"]
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//!
|
||||||
|
//! `Uuid` implements [`FromParam`] and [`FromFormField`] (i.e,
|
||||||
|
//! [`FromForm`](crate::form::FromForm)), allowing UUID values to be accepted
|
||||||
|
//! directly in paths, queries, and forms. You can use the `Uuid` type directly
|
||||||
|
//! as a target of a dynamic parameter:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[macro_use] extern crate rocket;
|
||||||
|
//! use rocket::serde::uuid::Uuid;
|
||||||
|
//!
|
||||||
|
//! #[get("/users/<id>")]
|
||||||
|
//! fn user(id: Uuid) -> String {
|
||||||
|
//! format!("We found: {}", id)
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! You can also use the `Uuid` as a form value, including in query strings:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[macro_use] extern crate rocket;
|
||||||
|
//! use rocket::serde::uuid::Uuid;
|
||||||
|
//!
|
||||||
|
//! #[get("/user?<id>")]
|
||||||
|
//! fn user(id: Uuid) -> String {
|
||||||
|
//! format!("User ID: {}", id)
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Additionally, `Uuid` implements `UriDisplay<P>` for all `P`. As such, route
|
||||||
|
//! URIs including `Uuid`s can be generated in a type-safe manner:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[macro_use] extern crate rocket;
|
||||||
|
//! use rocket::serde::uuid::Uuid;
|
||||||
|
//! use rocket::response::Redirect;
|
||||||
|
//!
|
||||||
|
//! #[get("/user/<id>")]
|
||||||
|
//! fn user(id: Uuid) -> String {
|
||||||
|
//! format!("User ID: {}", id)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[get("/user?<id>")]
|
||||||
|
//! fn old_user_path(id: Uuid) -> Redirect {
|
||||||
|
//! # let _ = Redirect::to(uri!(user(&id)));
|
||||||
|
//! # let _ = Redirect::to(uri!(old_user_path(id)));
|
||||||
|
//! # let _ = Redirect::to(uri!(old_user_path(&id)));
|
||||||
|
//! Redirect::to(uri!(user(id)))
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Extra Features
|
||||||
|
//!
|
||||||
|
//! The [`uuid`](https://docs.rs/uuid/0.8) crate exposes extra `v{n}` features
|
||||||
|
//! for generating UUIDs which are not enabled by Rocket. To enable these
|
||||||
|
//! features, depend on `uuid` directly. The extra functionality can be accessed
|
||||||
|
//! via both `rocket::serde::uuid::Uuid` or the direct `uuid::Uuid`; the types
|
||||||
|
//! are one and the same.
|
||||||
|
//!
|
||||||
|
//! ```toml
|
||||||
|
//! [dependencies.uuid]
|
||||||
|
//! version = "0.8"
|
||||||
|
//! features = ["v1", "v4"]
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::request::FromParam;
|
||||||
|
use crate::form::{self, FromFormField, ValueField};
|
||||||
|
|
||||||
|
/// A Universally Unique Identifier (UUID).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// To parse a UUID and print it as a urn:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::serde::uuid::{Uuid, Error};
|
||||||
|
///
|
||||||
|
/// # fn f() -> Result<(), Error> {
|
||||||
|
/// let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8")?;
|
||||||
|
/// println!("{}", uuid.to_urn());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub use _uuid::Uuid;
|
||||||
|
|
||||||
|
pub use _uuid::{Builder, Variant, Version};
|
||||||
|
|
||||||
|
/// Type alias for the error returned on [`FromParam`] or [`FromFormField`]
|
||||||
|
/// failure.
|
||||||
|
pub type Error = <_uuid::Uuid as std::str::FromStr>::Err;
|
||||||
|
|
||||||
|
impl<'a> FromParam<'a> for Uuid {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
/// A value is successfully parsed if `param` is a properly formatted Uuid.
|
||||||
|
/// Otherwise, an error is returned.
|
||||||
|
#[inline(always)]
|
||||||
|
fn from_param(param: &'a str) -> Result<Uuid, Self::Error> {
|
||||||
|
param.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'v> FromFormField<'v> for Uuid {
|
||||||
|
#[inline]
|
||||||
|
fn from_value(field: ValueField<'v>) -> form::Result<'v, Self> {
|
||||||
|
Ok(field.value.parse().map_err(form::error::Error::custom)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{Uuid, FromParam};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_param() {
|
||||||
|
let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2";
|
||||||
|
let uuid_wrapper = Uuid::from_param(uuid_str.into()).unwrap();
|
||||||
|
assert_eq!(uuid_str, uuid_wrapper.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "InvalidLength")]
|
||||||
|
fn test_from_param_invalid() {
|
||||||
|
let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2p";
|
||||||
|
Uuid::from_param(uuid_str.into()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ members = [
|
||||||
"templating",
|
"templating",
|
||||||
"testing",
|
"testing",
|
||||||
"tls",
|
"tls",
|
||||||
"uuid",
|
|
||||||
|
|
||||||
"pastebin",
|
"pastebin",
|
||||||
"todo",
|
"todo",
|
||||||
|
|
|
@ -60,7 +60,7 @@ This directory contains projects showcasing Rocket's features.
|
||||||
|
|
||||||
* **[`serialization`](./serialization)** - Showcases JSON and MessagePack
|
* **[`serialization`](./serialization)** - Showcases JSON and MessagePack
|
||||||
(de)serialization support by implementing a CRUD-like message API in JSON
|
(de)serialization support by implementing a CRUD-like message API in JSON
|
||||||
and a simply read/echo API in MessagePack.
|
and a simply read/echo API in MessagePack. Showcases UUID parsing support.
|
||||||
|
|
||||||
* **[`state`](./state)** - Illustrates the use of request-local state and
|
* **[`state`](./state)** - Illustrates the use of request-local state and
|
||||||
managed state. Uses request-local state to cache "expensive" per-request
|
managed state. Uses request-local state to cache "expensive" per-request
|
||||||
|
@ -80,6 +80,3 @@ This directory contains projects showcasing Rocket's features.
|
||||||
|
|
||||||
* **[`tls`](./tls)** - Illustrates configuring TLS with a variety of key pair
|
* **[`tls`](./tls)** - Illustrates configuring TLS with a variety of key pair
|
||||||
kinds.
|
kinds.
|
||||||
|
|
||||||
* **[`uuid`](./uuid)** - Uses UUID support in `contrib`, converting between
|
|
||||||
`contrib::Uuid` type and the `uuid` crate `Uuid`.
|
|
||||||
|
|
|
@ -7,4 +7,4 @@ publish = false
|
||||||
|
|
||||||
[dependencies.rocket]
|
[dependencies.rocket]
|
||||||
path = "../../core/lib"
|
path = "../../core/lib"
|
||||||
features = ["json", "msgpack"]
|
features = ["json", "msgpack", "uuid"]
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
|
|
||||||
mod json;
|
mod json;
|
||||||
mod msgpack;
|
mod msgpack;
|
||||||
|
mod uuid;
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> _ {
|
fn rocket() -> _ {
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.attach(json::stage())
|
.attach(json::stage())
|
||||||
.attach(msgpack::stage())
|
.attach(msgpack::stage())
|
||||||
|
.attach(uuid::stage())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use rocket::local::blocking::Client;
|
use rocket::local::blocking::Client;
|
||||||
use rocket::http::{Status, ContentType, Accept};
|
use rocket::http::{Status, ContentType, Accept};
|
||||||
use rocket::serde::{Serialize, Deserialize};
|
use rocket::serde::{Serialize, Deserialize, uuid::Uuid};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
|
@ -114,3 +114,25 @@ fn msgpack_post() {
|
||||||
assert_eq!(res.status(), Status::Ok);
|
assert_eq!(res.status(), Status::Ok);
|
||||||
assert_eq!(res.into_string().unwrap(), "Goodbye, world!");
|
assert_eq!(res.into_string().unwrap(), "Goodbye, world!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uuid() {
|
||||||
|
let client = Client::tracked(super::rocket()).unwrap();
|
||||||
|
|
||||||
|
let pairs = &[
|
||||||
|
("7f205202-7ba1-4c39-b2fc-3e630722bf9f", "We found: Lacy"),
|
||||||
|
("4da34121-bc7d-4fc1-aee6-bf8de0795333", "We found: Bob"),
|
||||||
|
("ad962969-4e3d-4de7-ac4a-2d86d6d10839", "We found: George"),
|
||||||
|
("e18b3a5c-488f-4159-a240-2101e0da19fd",
|
||||||
|
"Missing person for UUID: e18b3a5c-488f-4159-a240-2101e0da19fd"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (uuid, response) in pairs {
|
||||||
|
let uuid = Uuid::parse_str(uuid).unwrap();
|
||||||
|
let res = client.get(uri!(super::uuid::people(uuid))).dispatch();
|
||||||
|
assert_eq!(res.into_string().unwrap(), *response);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = client.get("/people/not-a-uuid").dispatch();
|
||||||
|
assert_eq!(res.status(), Status::NotFound);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use rocket::State;
|
||||||
|
use rocket::serde::uuid::Uuid;
|
||||||
|
|
||||||
|
// A small people mapping in managed state for the sake of this example. In a
|
||||||
|
// real application this would be a database.
|
||||||
|
struct People(HashMap<Uuid, &'static str>);
|
||||||
|
|
||||||
|
#[get("/people/<id>")]
|
||||||
|
fn people(id: Uuid, people: &State<People>) -> Result<String, String> {
|
||||||
|
Ok(people.0.get(&id)
|
||||||
|
.map(|person| format!("We found: {}", person))
|
||||||
|
.ok_or_else(|| format!("Missing person for UUID: {}", id))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stage() -> rocket::fairing::AdHoc {
|
||||||
|
// Seed the "database".
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("7f205202-7ba1-4c39-b2fc-3e630722bf9f".parse().unwrap(), "Lacy");
|
||||||
|
map.insert("4da34121-bc7d-4fc1-aee6-bf8de0795333".parse().unwrap(), "Bob");
|
||||||
|
map.insert("ad962969-4e3d-4de7-ac4a-2d86d6d10839".parse().unwrap(), "George");
|
||||||
|
|
||||||
|
rocket::fairing::AdHoc::on_ignite("UUID", |rocket| async {
|
||||||
|
rocket
|
||||||
|
.manage(People(map))
|
||||||
|
.mount("/", routes![people])
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "uuid"
|
|
||||||
version = "0.1.0"
|
|
||||||
workspace = "../"
|
|
||||||
edition = "2018"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rocket = { path = "../../core/lib" }
|
|
||||||
|
|
||||||
[dependencies.rocket_contrib]
|
|
||||||
default-features = false
|
|
||||||
path = "../../contrib/lib"
|
|
||||||
features = ["uuid"]
|
|
|
@ -1,36 +0,0 @@
|
||||||
#[macro_use] extern crate rocket;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use rocket::State;
|
|
||||||
use rocket_contrib::uuid::Uuid;
|
|
||||||
use rocket_contrib::uuid::extern_uuid;
|
|
||||||
|
|
||||||
#[cfg(test)] mod tests;
|
|
||||||
|
|
||||||
// A small people mapping in managed state for the sake of this example. In a
|
|
||||||
// real application this would be a database. Notice that we use the external
|
|
||||||
// Uuid type here and not the rocket_contrib::uuid::Uuid type. We do this purely
|
|
||||||
// for demonstrative purposes; in practice, we could use the contrib `Uuid`.
|
|
||||||
struct People(HashMap<extern_uuid::Uuid, &'static str>);
|
|
||||||
|
|
||||||
#[get("/people/<id>")]
|
|
||||||
fn people(id: Uuid, people: &State<People>) -> Result<String, String> {
|
|
||||||
// Because Uuid implements the Deref trait, we use Deref coercion to convert
|
|
||||||
// rocket_contrib::uuid::Uuid to uuid::Uuid.
|
|
||||||
Ok(people.0.get(&id)
|
|
||||||
.map(|person| format!("We found: {}", person))
|
|
||||||
.ok_or_else(|| format!("Person not found for UUID: {}", id))?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[launch]
|
|
||||||
fn rocket() -> _ {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert("7f205202-7ba1-4c39-b2fc-3e630722bf9f".parse().unwrap(), "Lacy");
|
|
||||||
map.insert("4da34121-bc7d-4fc1-aee6-bf8de0795333".parse().unwrap(), "Bob");
|
|
||||||
map.insert("ad962969-4e3d-4de7-ac4a-2d86d6d10839".parse().unwrap(), "George");
|
|
||||||
|
|
||||||
rocket::build()
|
|
||||||
.manage(People(map))
|
|
||||||
.mount("/", routes![people])
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
use super::rocket;
|
|
||||||
use rocket::local::blocking::Client;
|
|
||||||
use rocket::http::Status;
|
|
||||||
|
|
||||||
fn test(uri: &str, expected: &str) {
|
|
||||||
let client = Client::tracked(rocket()).unwrap();
|
|
||||||
let res = client.get(uri).dispatch();
|
|
||||||
assert_eq!(res.into_string(), Some(expected.into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_404(uri: &str) {
|
|
||||||
let client = Client::tracked(rocket()).unwrap();
|
|
||||||
let res = client.get(uri).dispatch();
|
|
||||||
assert_eq!(res.status(), Status::NotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_people() {
|
|
||||||
test("/people/7f205202-7ba1-4c39-b2fc-3e630722bf9f", "We found: Lacy");
|
|
||||||
test("/people/4da34121-bc7d-4fc1-aee6-bf8de0795333", "We found: Bob");
|
|
||||||
test("/people/ad962969-4e3d-4de7-ac4a-2d86d6d10839", "We found: George");
|
|
||||||
test("/people/e18b3a5c-488f-4159-a240-2101e0da19fd",
|
|
||||||
"Person not found for UUID: e18b3a5c-488f-4159-a240-2101e0da19fd");
|
|
||||||
test_404("/people/invalid_uuid");
|
|
||||||
}
|
|
|
@ -89,6 +89,7 @@ function test_core() {
|
||||||
tls
|
tls
|
||||||
json
|
json
|
||||||
msgpack
|
msgpack
|
||||||
|
uuid
|
||||||
)
|
)
|
||||||
|
|
||||||
pushd "${CORE_LIB_ROOT}" > /dev/null 2>&1
|
pushd "${CORE_LIB_ROOT}" > /dev/null 2>&1
|
||||||
|
|
Loading…
Reference in New Issue