aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-01-26 16:56:59 +0100
committerMartin Fischer <martin@push-f.com>2021-01-26 17:01:30 +0100
commit6c44846f6bf5901eaa12b943646632bd2163c898 (patch)
tree801cd2dfdd390a9a0c8fc63c9e4b1fa614cade22
parentcfb33b7f441e68161032b6505502b6490c4c0a3d (diff)
add request::Flash (inspired by Rocket)v0.3.4
bump version to 0.3.4
-rw-r--r--Cargo.toml2
-rw-r--r--src/request.rs65
2 files changed, 64 insertions, 3 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 91ced92..278de4e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "sputnik"
-version = "0.3.3"
+version = "0.3.4"
authors = ["Martin Fischer <martin@push-f.com>"]
license = "MIT"
description = "A lightweight layer on top of hyper to facilitate building web applications."
diff --git a/src/request.rs b/src/request.rs
index f2392b8..195e601 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -9,7 +9,7 @@ use std::{collections::HashMap, sync::Arc};
use rand::{Rng, distributions::Alphanumeric};
use async_trait::async_trait;
-use crate::response::SputnikHeaders;
+use crate::response::{SputnikHeaders, delete_cookie};
const CSRF_COOKIE_NAME : &str = "csrf";
@@ -88,6 +88,67 @@ impl std::fmt::Display for CsrfToken {
}
}
+const FLASH_COOKIE_NAME: &str = "flash";
+
+/// Show the user a message after redirecting them.
+pub struct Flash {
+ name: String,
+ message: String,
+}
+
+impl From<Flash> for Cookie<'_> {
+ fn from(flash: Flash) -> Self {
+ Cookie::build(FLASH_COOKIE_NAME, format!("{}:{}", flash.name, flash.message))
+ .max_age(Duration::minutes(5)).finish()
+ }
+}
+
+impl Flash {
+ /// If the request has a flash cookie retrieve it and append a set-cookie
+ /// header to delete the cookie to [`SputnikParts::response_headers`].
+ pub fn from_request(req: &mut Parts) -> Option<Self> {
+ req.cookies().get(FLASH_COOKIE_NAME)
+ .and_then(|cookie| {
+ req.response_headers().set_cookie(delete_cookie(FLASH_COOKIE_NAME));
+ let mut iter = cookie.value().splitn(2, ':');
+ if let (Some(name), Some(message)) = (iter.next(), iter.next()) {
+ return Some(Flash{name: name.to_owned(), message: message.to_owned()})
+ }
+ None
+ })
+ }
+
+ /// Constructs a new Flash message. The name must not contain a colon (`:`).
+ pub fn new(name: String, message: String) -> Self {
+ Flash{name, message}
+ }
+
+ /// Constructs a new "success" Flash message.
+ pub fn success(message: String) -> Self {
+ Flash{name: "success".to_owned(), message}
+ }
+
+ /// Constructs a new "warning" Flash message.
+ pub fn warning(message: String) -> Self {
+ Flash{name: "warning".to_owned(), message}
+ }
+
+ /// Constructs a new "error" Flash message.
+ pub fn error(message: String) -> Self {
+ Flash{name: "error".to_owned(), message}
+ }
+
+ /// Returns the name of the Flash message.
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ /// Returns the message of the Flash message.
+ pub fn message(&self) -> &str {
+ &self.message
+ }
+}
+
impl CsrfToken {
/// Returns a CSRF token, either extracted from the `csrf` cookie or newly
/// generated if the cookie wasn't sent (in which case a set-cookie header is
@@ -96,7 +157,7 @@ impl CsrfToken {
/// If there is no cookie, calling this method multiple times only generates
/// a new token on the first call, further calls return the previously
/// generated token.
- pub fn from_request(req: &mut Parts) -> CsrfToken {
+ pub fn from_request(req: &mut Parts) -> Self {
if let Some(token) = req.extensions.get::<CsrfToken>() {
return token.clone()
}