use super::{Url, Time};
use super::scope::Scope;
use std::borrow::{Cow, ToOwned};
use std::collections::HashMap;
use std::collections::hash_map::Iter;
use std::rc::Rc;
use std::sync::Arc;
pub trait GrantExtension {
fn identifier(&self) -> &'static str;
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
Public(Option<String>),
Private(Option<String>),
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct Extensions {
extensions: HashMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Grant {
pub owner_id: String,
pub client_id: String,
pub scope: Scope,
pub redirect_uri: Url,
pub until: Time,
pub extensions: Extensions,
}
impl Value {
pub fn public(content: Option<String>) -> Self {
Value::Public(content)
}
pub fn private(content: Option<String>) -> Value {
Value::Private(content)
}
pub fn public_value(&self) -> Result<Option<&str>, ()> {
match self {
Value::Public(Some(content)) => Ok(Some(&content)),
Value::Public(None) => Ok(None),
_ => Err(()),
}
}
pub fn into_public_value(self) -> Result<Option<String>, ()> {
match self {
Value::Public(content) => Ok(content),
_ => Err(()),
}
}
pub fn private_value(&self) -> Result<Option<&str>, ()> {
match self {
Value::Private(Some(content)) => Ok(Some(&content)),
Value::Private(None) => Ok(None),
_ => Err(()),
}
}
pub fn into_private_value(self) -> Result<Option<String>, ()> {
match self {
Value::Private(content) => Ok(content),
_ => Err(()),
}
}
}
impl Extensions {
pub fn new() -> Extensions {
Extensions::default()
}
pub fn set(&mut self, extension: &dyn GrantExtension, content: Value) {
self.extensions
.insert(extension.identifier().to_string(), content);
}
pub fn set_raw(&mut self, identifier: String, content: Value) {
self.extensions.insert(identifier, content);
}
pub fn remove(&mut self, extension: &dyn GrantExtension) -> Option<Value> {
self.extensions.remove(extension.identifier())
}
pub fn public(&self) -> PublicExtensions {
PublicExtensions {
iter: self.extensions.iter(),
}
}
pub fn private(&self) -> PrivateExtensions {
PrivateExtensions(self.extensions.iter())
}
}
pub struct PublicExtensions<'a> {
iter: Iter<'a, String, Value>,
}
pub struct PrivateExtensions<'a>(Iter<'a, String, Value>);
impl<'a> Iterator for PublicExtensions<'a> {
type Item = (&'a str, Option<&'a str>);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (key, value) = self.iter.next()?;
if let Ok(value) = value.public_value() {
return Some((key, value));
}
}
}
}
impl<'a> Iterator for PrivateExtensions<'a> {
type Item = (&'a str, Option<&'a str>);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (key, value) = self.0.next()?;
if let Ok(value) = value.private_value() {
return Some((key, value));
}
}
}
}
impl<'a, T: GrantExtension + ?Sized> GrantExtension for &'a T {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
impl<'a, T: GrantExtension + ?Sized> GrantExtension for Cow<'a, T>
where
T: Clone + ToOwned,
{
fn identifier(&self) -> &'static str {
self.as_ref().identifier()
}
}
impl<T: GrantExtension + ?Sized> GrantExtension for Box<T> {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
impl<T: GrantExtension + ?Sized> GrantExtension for Arc<T> {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
impl<T: GrantExtension + ?Sized> GrantExtension for Rc<T> {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
#[cfg(test)]
mod tests {
use super::{Extensions, Value};
#[test]
fn iteration() {
let mut extensions = Extensions::new();
extensions.set_raw("pub".into(), Value::Public(Some("content".into())));
extensions.set_raw("pub_none".into(), Value::Public(None));
extensions.set_raw("priv".into(), Value::Private(Some("private".into())));
extensions.set_raw("priv_none".into(), Value::Private(None));
assert_eq!(
extensions
.public()
.filter(|&(name, value)| name == "pub" && value == Some("content"))
.count(),
1
);
assert_eq!(
extensions
.public()
.filter(|&(name, value)| name == "pub_none" && value == None)
.count(),
1
);
assert_eq!(extensions.public().count(), 2);
assert_eq!(
extensions
.private()
.filter(|&(name, value)| name == "priv" && value == Some("private"))
.count(),
1
);
assert_eq!(
extensions
.private()
.filter(|&(name, value)| name == "priv_none" && value == None)
.count(),
1
);
assert_eq!(extensions.private().count(), 2);
}
}