working
This commit is contained in:
parent
e69bcfc23d
commit
b09e8eafbb
16 changed files with 2531 additions and 258 deletions
|
|
@ -1,5 +1,5 @@
|
|||
use restson::{Error, Response, RestClient, RestPath};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use restson::{RestClient, RestPath, Error, Response};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize)]
|
||||
struct Email<'a> {
|
||||
|
|
@ -37,8 +37,8 @@ impl<'a> RestPath<()> for Token<'a> {
|
|||
}
|
||||
|
||||
pub mod token {
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::util::DateTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -114,14 +114,25 @@ where
|
|||
Ok(tokens.into_inner())
|
||||
}
|
||||
|
||||
pub fn email<'a>(client: &'a RestClient, email: &'a str, password: &'a str) -> impl Future<Output=Result<Tokens, Error>> + 'a {
|
||||
pub fn email<'a>(
|
||||
client: &'a RestClient,
|
||||
email: &'a str,
|
||||
password: &'a str,
|
||||
) -> impl Future<Output = Result<Tokens, Error>> + 'a {
|
||||
authenticate(client, Email { email, password })
|
||||
}
|
||||
|
||||
pub fn phone<'a>(client: &'a RestClient, phone: &'a str, password: &'a str) -> impl Future<Output=Result<Tokens, Error>> + 'a {
|
||||
pub fn phone<'a>(
|
||||
client: &'a RestClient,
|
||||
phone: &'a str,
|
||||
password: &'a str,
|
||||
) -> impl Future<Output = Result<Tokens, Error>> + 'a {
|
||||
authenticate(client, Phone { phone, password })
|
||||
}
|
||||
|
||||
pub fn token<'a>(client: &'a RestClient, token: &'a str) -> impl Future<Output=Result<Tokens, Error>> + 'a {
|
||||
pub fn token<'a>(
|
||||
client: &'a RestClient,
|
||||
token: &'a str,
|
||||
) -> impl Future<Output = Result<Tokens, Error>> + 'a {
|
||||
authenticate(client, Token { token })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,16 +58,13 @@ impl<'a> Query<'a> {
|
|||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(Default)]
|
||||
pub enum Order {
|
||||
#[default]
|
||||
Ascending,
|
||||
Descending,
|
||||
}
|
||||
|
||||
impl Default for Order {
|
||||
fn default() -> Self {
|
||||
Self::Ascending
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Order {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
|
|
|||
|
|
@ -37,8 +37,5 @@ pub async fn identity(client: &RestClient) -> Result<Profile, Error> {
|
|||
}
|
||||
|
||||
pub async fn with_id(client: &RestClient, id: Id) -> Result<Profile, Error> {
|
||||
Ok(client
|
||||
.get_with::<_, Profile>(id, &[])
|
||||
.await?
|
||||
.into_inner())
|
||||
Ok(client.get_with::<_, Profile>(id, &[]).await?.into_inner())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,10 @@ impl Spond {
|
|||
#[builder]
|
||||
pub fn response(
|
||||
&self,
|
||||
#[builder(start_fn)]
|
||||
member: MemberId,
|
||||
#[builder(finish_fn)]
|
||||
client: &RestClient,
|
||||
#[builder(default = true)]
|
||||
accepted: bool,
|
||||
) -> impl Future<Output=Result<Responses, Error>> {
|
||||
#[builder(start_fn)] member: MemberId,
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
#[builder(default = true)] accepted: bool,
|
||||
) -> impl Future<Output = Result<Responses, Error>> {
|
||||
response(self.id)
|
||||
.member(member)
|
||||
.accepted(accepted)
|
||||
|
|
@ -78,27 +75,21 @@ impl Spond {
|
|||
}
|
||||
|
||||
#[builder]
|
||||
pub fn accept(&self,
|
||||
#[builder(start_fn)]
|
||||
member: MemberId,
|
||||
#[builder(finish_fn)]
|
||||
client: &RestClient,
|
||||
) -> impl Future<Output=Result<Responses, Error>> {
|
||||
self.response(member)
|
||||
.accepted(true)
|
||||
.call(client)
|
||||
pub fn accept(
|
||||
&self,
|
||||
#[builder(start_fn)] member: MemberId,
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
) -> impl Future<Output = Result<Responses, Error>> {
|
||||
self.response(member).accepted(true).call(client)
|
||||
}
|
||||
|
||||
#[builder]
|
||||
pub fn decline(&self,
|
||||
#[builder(start_fn)]
|
||||
member: MemberId,
|
||||
#[builder(finish_fn)]
|
||||
client: &RestClient,
|
||||
) -> impl Future<Output=Result<Responses, Error>> {
|
||||
self.response(member)
|
||||
.accepted(false)
|
||||
.call(client)
|
||||
pub fn decline(
|
||||
&self,
|
||||
#[builder(start_fn)] member: MemberId,
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
) -> impl Future<Output = Result<Responses, Error>> {
|
||||
self.response(member).accepted(false).call(client)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -198,11 +189,8 @@ pub fn decline(
|
|||
#[builder(start_fn)] spond: Id,
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
member: MemberId,
|
||||
) -> impl std::future::Future<Output=Result<Responses, Error>> {
|
||||
response(spond)
|
||||
.member(member)
|
||||
.accepted(false)
|
||||
.call(client)
|
||||
) -> impl std::future::Future<Output = Result<Responses, Error>> {
|
||||
response(spond).member(member).accepted(false).call(client)
|
||||
}
|
||||
|
||||
#[bon::builder]
|
||||
|
|
@ -210,11 +198,8 @@ pub fn accept(
|
|||
#[builder(start_fn)] spond: Id,
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
member: MemberId,
|
||||
) -> impl std::future::Future<Output=Result<Responses, Error>> {
|
||||
response(spond)
|
||||
.member(member)
|
||||
.accepted(true)
|
||||
.call(client)
|
||||
) -> impl std::future::Future<Output = Result<Responses, Error>> {
|
||||
response(spond).member(member).accepted(true).call(client)
|
||||
}
|
||||
|
||||
#[bon::builder]
|
||||
|
|
@ -236,7 +221,8 @@ pub async fn response(
|
|||
}
|
||||
}
|
||||
|
||||
let request = Request{accepted};
|
||||
let response: restson::Response<Responses> = client.put_capture((spond, member), &request).await?;
|
||||
let request = Request { accepted };
|
||||
let response: restson::Response<Responses> =
|
||||
client.put_capture((spond, member), &request).await?;
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
|
|
|||
33
api/src/util/chrono/datetime.rs
Normal file
33
api/src/util/chrono/datetime.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use serde::{Deserialize, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct DateTime(chrono::DateTime<chrono::Utc>);
|
||||
|
||||
impl Serialize for DateTime {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DateTime {
|
||||
fn default() -> Self {
|
||||
chrono::Utc::now().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DateTime {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.0.to_rfc3339_opts(chrono::SecondsFormat::Secs, true,)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<chrono::DateTime<chrono::Utc>> for DateTime {
|
||||
fn from(dt: chrono::DateTime<chrono::Utc>) -> Self {
|
||||
Self(dt)
|
||||
}
|
||||
}
|
||||
5
api/src/util/chrono/mod.rs
Normal file
5
api/src/util/chrono/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
mod datetime;
|
||||
pub use datetime::DateTime;
|
||||
|
||||
mod timestamp;
|
||||
pub use timestamp::Timestamp;
|
||||
51
api/src/util/chrono/timestamp.rs
Normal file
51
api/src/util/chrono/timestamp.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
use chrono::TimeZone;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer}; // for timestamp_millis_opt
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Timestamp(chrono::DateTime<chrono::Utc>);
|
||||
|
||||
impl Serialize for Timestamp {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_i64(self.0.timestamp_millis())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Timestamp {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let millis = i64::deserialize(deserializer)?;
|
||||
let dt = chrono::Utc
|
||||
.timestamp_millis_opt(millis)
|
||||
.single()
|
||||
.ok_or_else(|| serde::de::Error::custom("invalid timestamp"))?;
|
||||
|
||||
Ok(Timestamp(dt))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Timestamp {
|
||||
fn default() -> Self {
|
||||
chrono::Utc::now().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Timestamp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.0.to_rfc3339_opts(chrono::SecondsFormat::Secs, true,)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Into<chrono::DateTime<chrono::Utc>>> From<I> for Timestamp {
|
||||
fn from(dt: I) -> Self {
|
||||
Self(dt.into())
|
||||
}
|
||||
}
|
||||
114
api/src/util/id.rs
Normal file
114
api/src/util/id.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
fn short_type_name(full: &str) -> &str {
|
||||
let generic = full.find('<').unwrap_or(full.len());
|
||||
let last_colon = full[..generic].rfind("::").map_or(0, |idx| idx + 2);
|
||||
|
||||
&full[last_colon..]
|
||||
}
|
||||
|
||||
pub trait Type: Sized {
|
||||
type Type: fmt::Display + Copy;
|
||||
|
||||
fn name() -> &'static str {
|
||||
short_type_name(std::any::type_name::<Self>())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Id<T: Type>(T::Type);
|
||||
|
||||
impl<T: Type> AsRef<T::Type> for Id<T> {
|
||||
fn as_ref(&self) -> &T::Type {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> Copy for Id<T> {}
|
||||
|
||||
impl<T: Type> Clone for Id<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> Id<T> {
|
||||
pub fn new(src: T::Type) -> Self {
|
||||
Self(src)
|
||||
}
|
||||
|
||||
pub async fn load(
|
||||
&self,
|
||||
client: &restson::RestClient,
|
||||
) -> Result<T, restson::Error>
|
||||
where
|
||||
T: restson::RestPath<Id<T>>,
|
||||
T: serde::de::DeserializeOwned,
|
||||
{ Ok(client.get_with::<_, T>(*self, &[]).await?.into_inner()) }
|
||||
}
|
||||
|
||||
impl<T: Type> fmt::Debug for Id<T>
|
||||
where
|
||||
T::Type: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ID<{}:{}>", T::name(), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> fmt::Display for Id<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Serialize for Id<T>
|
||||
where
|
||||
T: Type,
|
||||
T::Type: Serialize,
|
||||
{
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for Id<T>
|
||||
where
|
||||
T: Type,
|
||||
T::Type: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Self(T::Type::deserialize(deserializer)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Id<T>
|
||||
where
|
||||
T: Type,
|
||||
T::Type: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Id<T>
|
||||
where
|
||||
T: Type,
|
||||
T::Type: Eq,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> Hash for Id<T>
|
||||
where
|
||||
T: Type,
|
||||
T::Type: Hash,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state)
|
||||
}
|
||||
}
|
||||
11
api/src/util/mod.rs
Normal file
11
api/src/util/mod.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pub mod chrono;
|
||||
pub use chrono::{DateTime, Timestamp};
|
||||
|
||||
pub mod x128;
|
||||
pub use x128::X128;
|
||||
|
||||
pub mod id;
|
||||
pub use id::Id;
|
||||
|
||||
pub mod visibility;
|
||||
pub use visibility::Visibility;
|
||||
36
api/src/util/visibility.rs
Normal file
36
api/src/util/visibility.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Visibility {
|
||||
Invitees,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl Serialize for Visibility {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = match self {
|
||||
Visibility::Invitees => "INVITEES",
|
||||
Visibility::Unknown(other) => other,
|
||||
};
|
||||
|
||||
serializer.serialize_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Visibility {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
// Borrow when possible
|
||||
let s = <&str>::deserialize(deserializer)?;
|
||||
|
||||
Ok(match s {
|
||||
"INVITEES" => Visibility::Invitees,
|
||||
other => Visibility::Unknown(other.to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
84
api/src/util/x128.rs
Normal file
84
api/src/util/x128.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Wrapper for u128 that serializes/deserializes as 32-charachter hex
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct X128(u128);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("invalid length: {0}")]
|
||||
Length(usize),
|
||||
|
||||
#[error("parser error: {0}")]
|
||||
Parser(#[from] std::num::ParseIntError),
|
||||
}
|
||||
|
||||
impl X128 {
|
||||
/// construct a new value
|
||||
pub fn new(value: u128) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// access the inner value explicitely
|
||||
pub fn value(&self) -> u128 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<u128>> From<T> for X128 {
|
||||
fn from(src: T) -> Self {
|
||||
Self::new(src.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for X128 {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Error> {
|
||||
match s.len() {
|
||||
32 => u128::from_str_radix(s, 16)
|
||||
.map(Self::new)
|
||||
.map_err(Error::Parser),
|
||||
len => Err(Error::Length(len)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//impl TryFrom<&str> for X128 {
|
||||
// type Error = Error;
|
||||
//
|
||||
// fn try_from(s: &str) -> Result<Self, Error> {
|
||||
// Self::from_str(s)
|
||||
// }
|
||||
//}
|
||||
|
||||
impl Deref for X128 {
|
||||
type Target = u128;
|
||||
|
||||
fn deref(&self) -> &u128 {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for X128 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:032X}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for X128 {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for X128 {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let s = String::deserialize(deserializer)?;
|
||||
|
||||
Self::from_str(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue