|
| 1 | +use std::collections::HashMap; |
| 2 | + |
| 3 | +use crate::localization::LOCALE_COOKIE_NAME; |
| 4 | +use pointercrate_core::error::CoreError; |
| 5 | +use pointercrate_core::localization::LocaleConfiguration; |
| 6 | +use rocket::{ |
| 7 | + http::CookieJar, |
| 8 | + request::{FromRequest, Outcome}, |
| 9 | + Request, |
| 10 | +}; |
| 11 | + |
| 12 | +/// A request guard which stores the preferences sent from the client. |
| 13 | +pub struct ClientPreferences<'k, 'v>(HashMap<&'k str, &'v str>); |
| 14 | + |
| 15 | +impl<'k: 'v, 'v> ClientPreferences<'k, 'v> { |
| 16 | + /// Retrieve a particular preference which was sent to us from the client. |
| 17 | + /// |
| 18 | + /// `T` must implement `From<ClientPreference>`, which [`String`] already |
| 19 | + /// implements, in case the untouched cookie value is what needs to be handled. |
| 20 | + pub fn get(&self, name: &'k str) -> Option<&'v str> { |
| 21 | + self.0.get(name).map(|&s| s) |
| 22 | + } |
| 23 | + |
| 24 | + pub fn from_cookies(cookies: &'v CookieJar<'v>, preference_manager: &'k PreferenceManager) -> Self { |
| 25 | + ClientPreferences( |
| 26 | + preference_manager |
| 27 | + .0 |
| 28 | + .iter() |
| 29 | + .map(|(name, default)| { |
| 30 | + ( |
| 31 | + name.as_ref(), |
| 32 | + cookies |
| 33 | + .get(&format!("preference-{}", name)) |
| 34 | + .map(|cookie| cookie.value()) |
| 35 | + .unwrap_or(default) |
| 36 | + .as_ref(), |
| 37 | + ) |
| 38 | + }) |
| 39 | + .collect(), |
| 40 | + ) |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +#[rocket::async_trait] |
| 45 | +impl<'r> FromRequest<'r> for ClientPreferences<'r, 'r> { |
| 46 | + type Error = CoreError; |
| 47 | + |
| 48 | + async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { |
| 49 | + let preference_manager = match request.rocket().state::<PreferenceManager>() { |
| 50 | + Some(preference_manager) => preference_manager, |
| 51 | + _ => return Outcome::Success(ClientPreferences(HashMap::new())), // return an empty preferences hashmap if this instance doesnt support preferences |
| 52 | + }; |
| 53 | + |
| 54 | + let preferences = ClientPreferences::from_cookies(request.cookies(), preference_manager); |
| 55 | + |
| 56 | + Outcome::Success(preferences) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +/// A configuration state to manage all of your pointercrate instance's |
| 61 | +/// client preferences. |
| 62 | +#[derive(Default)] |
| 63 | +pub struct PreferenceManager(HashMap<String, String>); |
| 64 | + |
| 65 | +impl PreferenceManager { |
| 66 | + /// Append a new preference to this [`PreferenceManager`]. `name` represents |
| 67 | + /// the name of the cookie which stores the value of this preference. |
| 68 | + /// |
| 69 | + /// Note that the cookie name is prefixed with `"preference-"`, so creating a |
| 70 | + /// preference with the `name` value as `"theme"` would result in the cookie |
| 71 | + /// sent from the client being named `"preference-theme"`. |
| 72 | + /// |
| 73 | + /// If the cookie was not received, its value will default to `default`. |
| 74 | + pub fn preference(mut self, name: impl Into<String>, default: impl Into<String>) -> Self { |
| 75 | + self.0.insert(name.into(), default.into()); |
| 76 | + |
| 77 | + self |
| 78 | + } |
| 79 | + |
| 80 | + /// Automatically register the preferences needed to store active locales. |
| 81 | + /// |
| 82 | + /// Requires the global localization context to have been set up via [`LocalesLoader::commit`], |
| 83 | + /// otherwise will panic. |
| 84 | + pub fn with_localization(self) -> Self { |
| 85 | + self.preference(LOCALE_COOKIE_NAME, LocaleConfiguration::get().fallback.as_str()) |
| 86 | + } |
| 87 | +} |
0 commit comments