#![allow(dead_code)]
use std::cell::{Ref, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use futures::{pin_mut, Stream, TryStreamExt};
use serde::de::{DeserializeOwned, Error as DeserError};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use super::{Error, ErrorKind, Result};
#[derive(Clone)]
pub struct Query(pub Vec<(String, String)>);
#[derive(Debug, Clone)]
pub struct ValueCache<T: Clone>(RefCell<Option<T>>);
#[derive(Debug, Clone)]
pub struct MapCache<K: Hash + Eq, V: Clone>(RefCell<HashMap<K, V>>);
impl fmt::Debug for Query {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
write!(f, "{:?}", self.0)
}
}
impl Query {
pub fn new() -> Query {
Query(Vec::new())
}
#[allow(clippy::needless_pass_by_value)] pub fn push<K, V>(&mut self, param: K, value: V)
where
K: Into<String>,
V: ToString,
{
self.0.push((param.into(), value.to_string()))
}
pub fn push_str<K, V>(&mut self, param: K, value: V)
where
K: Into<String>,
V: Into<String>,
{
self.0.push((param.into(), value.into()))
}
pub fn with_marker_and_limit(&self, limit: Option<usize>, marker: Option<String>) -> Query {
let mut new = self.clone();
if let Some(limit_) = limit {
new.push("limit", limit_);
}
if let Some(marker_) = marker {
new.push_str("marker", marker_);
}
new
}
}
impl Serialize for Query {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<T: Clone> ValueCache<T> {
pub fn new(value: Option<T>) -> ValueCache<T> {
ValueCache(RefCell::new(value))
}
pub fn ensure_value<F>(&self, default: F) -> Result<()>
where
F: FnOnce() -> Result<T>,
{
if self.0.borrow().is_some() {
return Ok(());
};
*self.0.borrow_mut() = Some(default()?);
Ok(())
}
pub fn validate<F>(&self, check: F) -> bool
where
F: FnOnce(&T) -> bool,
{
let valid = match self.0.borrow().as_ref() {
Some(v) => check(v),
None => false,
};
if !valid {
*self.0.borrow_mut() = None;
false
} else {
true
}
}
pub fn validate_and_ensure_value<V, F>(&self, check: V, default: F) -> Result<()>
where
V: FnOnce(&T) -> bool,
F: FnOnce() -> Result<T>,
{
if self.validate(check) {
Ok(())
} else {
self.ensure_value(default)
}
}
pub fn extract<F, R>(&self, filter: F) -> Option<R>
where
F: FnOnce(&T) -> R,
{
self.0.borrow().as_ref().map(filter)
}
}
impl<K: Hash + Eq, V: Clone> MapCache<K, V> {
pub fn new() -> MapCache<K, V> {
MapCache(RefCell::new(HashMap::new()))
}
pub fn ensure_value<F>(&self, key: K, default: F) -> Result<()>
where
F: FnOnce(&K) -> Result<V>,
{
if self.0.borrow().contains_key(&key) {
return Ok(());
}
let new = default(&key)?;
let _ = self.0.borrow_mut().insert(key, new);
Ok(())
}
pub fn get_ref(&self, key: &K) -> Option<Ref<V>> {
let map = self.0.borrow();
if map.contains_key(key) {
Some(Ref::map(map, |m| m.get(key).unwrap()))
} else {
None
}
}
}
pub fn one<T, I, S>(collection: I, not_found_msg: S, too_many_msg: S) -> Result<T>
where
I: IntoIterator<Item = T>,
S: Into<String>,
{
let mut iter = collection.into_iter();
let result = iter
.next()
.ok_or_else(|| Error::new(ErrorKind::ResourceNotFound, not_found_msg.into()))?;
if iter.next().is_some() {
Err(Error::new(ErrorKind::TooManyItems, too_many_msg.into()))
} else {
Ok(result)
}
}
pub fn endpoint_not_found<D: fmt::Display>(service_type: D) -> Error {
Error::new(
ErrorKind::EndpointNotFound,
format!("Endpoint for service {service_type} was not found"),
)
}
pub async fn try_one<T, S>(stream: S) -> Result<T>
where
S: Stream<Item = Result<T>>,
{
pin_mut!(stream);
match stream.try_next().await? {
Some(result) => {
if stream.try_next().await?.is_some() {
Err(Error::new(
ErrorKind::TooManyItems,
"Query returned more than one result",
))
} else {
Ok(result)
}
}
None => Err(Error::new(
ErrorKind::ResourceNotFound,
"Query returned no results",
)),
}
}
protocol_enum! {
#[allow(missing_docs)]
enum SortDir {
Asc = "asc",
Desc = "desc"
}
}
pub fn unit_to_null<S: Serializer>(s: S) -> std::result::Result<S::Ok, S::Error> {
s.serialize_none()
}
pub mod url {
use reqwest::Url;
#[inline]
#[allow(unused_results)]
pub fn is_root(url: &Url) -> bool {
url.path_segments().unwrap().any(|x| !x.is_empty())
}
#[inline]
#[allow(unused_results)]
pub fn join(mut url: Url, other: &str) -> Url {
url.path_segments_mut().unwrap().pop_if_empty().push(other);
url
}
#[inline]
#[allow(unused_results)]
pub fn extend<I>(mut url: Url, segments: I) -> Url
where
I: IntoIterator,
I::Item: AsRef<str>,
{
url.path_segments_mut()
.unwrap()
.pop_if_empty()
.extend(segments);
url
}
#[inline]
#[allow(unused_results)]
pub fn pop(mut url: Url, keep_slash: bool) -> Url {
url.path_segments_mut().unwrap().pop_if_empty().pop();
if keep_slash {
url.path_segments_mut().unwrap().pop_if_empty().push("");
}
url
}
}
#[inline]
pub fn some_truth() -> bool {
true
}
#[inline]
pub fn is_default<T: Default + PartialEq>(value: &T) -> bool {
*value != T::default()
}
pub fn empty_map_as_default<'de, D, T>(des: D) -> std::result::Result<T, D::Error>
where
D: Deserializer<'de>,
T: DeserializeOwned + Default,
{
let value = Value::deserialize(des)?;
match value {
Value::Object(ref s) if s.is_empty() => Ok(T::default()),
_ => serde_json::from_value(value).map_err(D::Error::custom),
}
}
#[cfg(test)]
mod test {
use serde::Deserialize;
use std::collections::HashMap;
use super::*;
#[derive(Debug, Deserialize)]
struct TestDeserialize {
#[serde(default = "some_truth")]
default_to_true: bool,
#[serde(default = "some_truth")]
actually_false: bool,
#[serde(deserialize_with = "empty_map_as_default")]
non_empty_map: Option<HashMap<String, String>>,
#[serde(deserialize_with = "empty_map_as_default")]
empty_map_as_none: Option<HashMap<String, String>>,
#[serde(default, deserialize_with = "empty_map_as_default")]
empty_map_as_none_with_default: Option<HashMap<String, String>>,
}
#[test]
fn test_deserialize() {
let json = r#"{
"actually_false": false,
"empty_map_as_none": {},
"non_empty_map": {"a": "b"}
}"#;
let result: TestDeserialize = serde_json::from_str(json).unwrap();
assert!(result.default_to_true);
assert!(!result.actually_false);
assert_eq!(result.non_empty_map.unwrap().len(), 1);
assert!(result.empty_map_as_none.is_none());
assert!(result.empty_map_as_none_with_default.is_none());
}
}