working example
This commit is contained in:
parent
aaf9781fe8
commit
e69bcfc23d
18 changed files with 1290 additions and 252 deletions
12
api/Cargo.toml
Normal file
12
api/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "spond-api"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
bon = "3.9.0"
|
||||
chrono = { version = "0.4.44", features = ["serde"] }
|
||||
restson = "1.5.0"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
thiserror = "2.0.18"
|
||||
127
api/src/authentication.rs
Normal file
127
api/src/authentication.rs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use restson::{RestClient, RestPath, Error, Response};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize)]
|
||||
struct Email<'a> {
|
||||
email: &'a str,
|
||||
password: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> RestPath<()> for Email<'a> {
|
||||
fn get_path(_: ()) -> Result<String, Error> {
|
||||
Ok(String::from("auth2/login"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize)]
|
||||
struct Phone<'a> {
|
||||
phone: &'a str,
|
||||
password: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> RestPath<()> for Phone<'a> {
|
||||
fn get_path(_: ()) -> Result<String, Error> {
|
||||
Ok(String::from("auth2/login"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize)]
|
||||
struct Token<'a> {
|
||||
token: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> RestPath<()> for Token<'a> {
|
||||
fn get_path(_: ()) -> Result<String, Error> {
|
||||
Ok(String::from("auth2/login/refresh"))
|
||||
}
|
||||
}
|
||||
|
||||
pub mod token {
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::util::DateTime;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Token {
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl AsRef<str> for Token {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.token
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct WithExpiration {
|
||||
#[serde(flatten)]
|
||||
pub token: Token,
|
||||
#[allow(unused)]
|
||||
pub expiration: DateTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Access(WithExpiration);
|
||||
|
||||
impl Deref for Access {
|
||||
type Target = WithExpiration;
|
||||
|
||||
fn deref(&self) -> &WithExpiration {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Refresh(WithExpiration);
|
||||
|
||||
impl Deref for Refresh {
|
||||
type Target = WithExpiration;
|
||||
fn deref(&self) -> &WithExpiration {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Password(WithExpiration);
|
||||
|
||||
impl Deref for Password {
|
||||
type Target = WithExpiration;
|
||||
fn deref(&self) -> &WithExpiration {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Tokens {
|
||||
#[serde(rename = "accessToken")]
|
||||
pub access: token::Access,
|
||||
#[serde(rename = "refreshToken")]
|
||||
pub refresh: token::Refresh,
|
||||
#[serde(rename = "passwordToken")]
|
||||
pub password: token::Password,
|
||||
}
|
||||
|
||||
async fn authenticate<R>(client: &RestClient, request: R) -> Result<Tokens, Error>
|
||||
where
|
||||
R: RestPath<()>,
|
||||
R: Serialize,
|
||||
{
|
||||
let tokens: Response<Tokens> = client.post_capture((), &request).await?;
|
||||
Ok(tokens.into_inner())
|
||||
}
|
||||
|
||||
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 {
|
||||
authenticate(client, Phone { phone, password })
|
||||
}
|
||||
|
||||
pub fn token<'a>(client: &'a RestClient, token: &'a str) -> impl Future<Output=Result<Tokens, Error>> + 'a {
|
||||
authenticate(client, Token { token })
|
||||
}
|
||||
39
api/src/group.rs
Normal file
39
api/src/group.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use super::GroupId as Id;
|
||||
use super::MemberId;
|
||||
use restson::{Error, RestPath};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Group {
|
||||
pub id: Id,
|
||||
|
||||
#[serde(flatten)]
|
||||
unknown: serde_json::Value,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Group {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&self.unknown).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RestPath<Id> for Group {
|
||||
fn get_path(id: Id) -> std::result::Result<String, Error> {
|
||||
Ok(format!("groups/{id}"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Member {
|
||||
pub id: MemberId,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub unknown: serde_json::Value,
|
||||
}
|
||||
impl super::util::id::Type for Member {
|
||||
type Type = super::util::X128;
|
||||
}
|
||||
123
api/src/lib.rs
Normal file
123
api/src/lib.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use std::collections::{
|
||||
HashMap as Map,
|
||||
//HashSet as Set,
|
||||
};
|
||||
|
||||
pub mod util;
|
||||
|
||||
pub mod authentication;
|
||||
|
||||
pub mod profile;
|
||||
pub use profile::Profile;
|
||||
pub type ProfileId = util::Id<Profile>;
|
||||
|
||||
pub mod spond;
|
||||
pub use spond::Spond;
|
||||
pub type SpondId = util::Id<Spond>;
|
||||
|
||||
pub mod series;
|
||||
pub use series::Series;
|
||||
pub type SeriesId = util::Id<Series>;
|
||||
|
||||
pub mod group;
|
||||
pub use group::Group;
|
||||
pub type GroupId = util::Id<Group>;
|
||||
pub type MemberId = util::Id<group::Member>;
|
||||
|
||||
impl<T: restson::RestPath<util::Id<T>>> util::id::Type for T {
|
||||
type Type = util::X128;
|
||||
}
|
||||
|
||||
//impl<T: restson::RestPath<(util::Id<T>,)> + serde::de::DeserializeOwned + util::id::Type>
|
||||
// util::Id<T>
|
||||
//{
|
||||
// pub async fn load(&self, client: &restson::RestClient) -> Result<T, restson::Error> {
|
||||
// Ok(client.get_with::<_, T>((*self,), &[]).await?.into_inner())
|
||||
// }
|
||||
//}
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Query<'a>(Vec<(&'a str, String)>);
|
||||
|
||||
impl<'a> Query<'a> {
|
||||
pub fn add(&mut self, key: &'a str, value: impl std::fmt::Display) {
|
||||
self.0.push((key, value.to_string()));
|
||||
}
|
||||
|
||||
pub fn maybe_add(&mut self, key: &'a str, value: Option<impl std::fmt::Display>) {
|
||||
if let Some(value) = value {
|
||||
self.add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
fn as_slice(&self) -> Vec<(&'a str, &str)> {
|
||||
self.0.iter().map(|(k, v)| (*k, v.as_str())).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum Order {
|
||||
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 {
|
||||
write!(f, "{}", self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Order {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
Self::Ascending => "asc",
|
||||
Self::Descending => "desc",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Order {
|
||||
fn from(ascending: bool) -> Self {
|
||||
if ascending {
|
||||
Self::Ascending
|
||||
} else {
|
||||
Self::Descending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeclineMessage {
|
||||
pub message: String,
|
||||
pub profile_id: MemberId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Responses {
|
||||
#[serde(default)]
|
||||
pub accepted_ids: Vec<MemberId>,
|
||||
#[serde(default)]
|
||||
pub declined_ids: Vec<MemberId>,
|
||||
#[serde(default)]
|
||||
pub waitinglist_ids: Vec<MemberId>,
|
||||
#[serde(default)]
|
||||
pub unanswered_ids: Vec<MemberId>,
|
||||
#[serde(default)]
|
||||
pub unconfirmed_ids: Vec<MemberId>,
|
||||
#[serde(default)]
|
||||
pub participant_ids: Vec<MemberId>,
|
||||
#[serde(default)]
|
||||
pub decline_messages: Map<MemberId, DeclineMessage>,
|
||||
#[serde(flatten)]
|
||||
pub unknown: Map<String, serde_json::Value>,
|
||||
}
|
||||
44
api/src/profile.rs
Normal file
44
api/src/profile.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use super::ProfileId as Id;
|
||||
use restson::{Error, RestClient, RestPath};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Profile {
|
||||
pub id: Id,
|
||||
|
||||
#[serde(flatten)]
|
||||
unknown: serde_json::Value,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Profile {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&self.unknown).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RestPath<Id> for Profile {
|
||||
fn get_path(id: Id) -> std::result::Result<String, Error> {
|
||||
Ok(format!("profile/{id}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl RestPath<()> for Profile {
|
||||
fn get_path(_: ()) -> std::result::Result<String, Error> {
|
||||
Ok("profile".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn identity(client: &RestClient) -> Result<Profile, Error> {
|
||||
Ok(client.get_with::<_, Profile>((), &[]).await?.into_inner())
|
||||
}
|
||||
|
||||
pub async fn with_id(client: &RestClient, id: Id) -> Result<Profile, Error> {
|
||||
Ok(client
|
||||
.get_with::<_, Profile>(id, &[])
|
||||
.await?
|
||||
.into_inner())
|
||||
}
|
||||
18
api/src/series.rs
Normal file
18
api/src/series.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
use restson::{Error, RestPath};
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::SeriesId as Id;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Series {
|
||||
pub id: Id,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub unknown: serde_json::Value,
|
||||
}
|
||||
|
||||
impl RestPath<Id> for Series {
|
||||
fn get_path(id: Id) -> std::result::Result<String, Error> {
|
||||
Ok(format!("series/{id}"))
|
||||
}
|
||||
}
|
||||
242
api/src/spond.rs
Normal file
242
api/src/spond.rs
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
use super::{
|
||||
MemberId, Order, ProfileId, Query, Responses, SeriesId, SpondId as Id,
|
||||
util::{DateTime, Timestamp, Visibility},
|
||||
};
|
||||
use restson::{Error, RestClient, RestPath};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Group {
|
||||
#[serde(flatten)]
|
||||
pub unknown: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Recipients {
|
||||
pub group: HashMap<String, serde_json::Value>,
|
||||
pub guardians: std::vec::Vec<serde_json::Value>,
|
||||
pub profiles: std::vec::Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Comment {
|
||||
pub id: crate::util::Id<Comment>,
|
||||
pub children: std::vec::Vec<serde_json::Value>,
|
||||
pub from_profile_id: ProfileId,
|
||||
pub reactions: HashMap<String, HashMap<ProfileId, Timestamp>>,
|
||||
pub text: String,
|
||||
pub timestamp: DateTime,
|
||||
}
|
||||
|
||||
impl crate::util::id::Type for Comment {
|
||||
type Type = crate::util::X128;
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Spond {
|
||||
pub id: Id,
|
||||
pub heading: String,
|
||||
pub series_id: Option<SeriesId>,
|
||||
pub responses: Responses,
|
||||
pub updated: Timestamp,
|
||||
pub recipients: Recipients,
|
||||
pub hidden: bool,
|
||||
pub comments: Vec<Comment>,
|
||||
pub auto_accept: bool,
|
||||
pub start_timestamp: DateTime,
|
||||
pub end_timestamp: DateTime,
|
||||
pub creator_id: ProfileId,
|
||||
pub visibility: Visibility,
|
||||
pub behalf_of_ids: Vec<MemberId>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub unknown: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
#[bon::bon]
|
||||
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>> {
|
||||
response(self.id)
|
||||
.member(member)
|
||||
.accepted(accepted)
|
||||
.call(client)
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Spond {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&self.unknown).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct Sponds(Vec<Spond>);
|
||||
|
||||
impl RestPath<()> for Sponds {
|
||||
fn get_path(_: ()) -> std::result::Result<String, Error> {
|
||||
Ok(String::from("sponds"))
|
||||
}
|
||||
}
|
||||
|
||||
impl RestPath<Id> for Spond {
|
||||
fn get_path(id: Id) -> std::result::Result<String, Error> {
|
||||
Ok(format!("sponds/{id}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl RestPath<()> for Spond {
|
||||
fn get_path(_: ()) -> std::result::Result<String, Error> {
|
||||
Ok("sponds".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[bon::builder]
|
||||
pub async fn spond(
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
#[builder(finish_fn)] id: Id,
|
||||
|
||||
// query flags
|
||||
include_comments: Option<bool>,
|
||||
add_profile_info: Option<bool>,
|
||||
) -> Result<Spond, Error> {
|
||||
let mut q = Query::default();
|
||||
q.maybe_add("includeComments", include_comments);
|
||||
q.maybe_add("addProfileInfo", add_profile_info);
|
||||
|
||||
Ok(client
|
||||
.get_with::<_, Spond>(id, &q.as_slice())
|
||||
.await?
|
||||
.into_inner())
|
||||
}
|
||||
|
||||
#[bon::builder]
|
||||
pub async fn search(
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
|
||||
// query flags
|
||||
include_comments: Option<bool>,
|
||||
comments: Option<bool>,
|
||||
hidden: Option<bool>,
|
||||
add_profile_info: Option<bool>,
|
||||
scheduled: Option<bool>,
|
||||
#[builder(default, into)] order: Order,
|
||||
#[builder(default = 20)] max: usize,
|
||||
#[builder(into)] min_end_timestamp: Option<DateTime>,
|
||||
#[builder(into)] max_end_timestamp: Option<DateTime>,
|
||||
#[builder(into)] min_start_timestamp: Option<DateTime>,
|
||||
#[builder(into)] max_start_timestamp: Option<DateTime>,
|
||||
prev_id: Option<Id>,
|
||||
series_id: Option<SeriesId>,
|
||||
) -> Result<Vec<Spond>, Error> {
|
||||
let mut q = Query::default();
|
||||
q.maybe_add("seriesId", series_id);
|
||||
q.maybe_add("includeComments", include_comments);
|
||||
q.maybe_add("comments", comments);
|
||||
q.maybe_add("addProfileInfo", add_profile_info);
|
||||
q.maybe_add("scheduled", scheduled);
|
||||
q.maybe_add("hidden", hidden);
|
||||
q.maybe_add("minEndTimestamp", min_end_timestamp);
|
||||
q.maybe_add("maxEndTimestamp", max_end_timestamp);
|
||||
q.maybe_add("minStartTimestamp", min_start_timestamp);
|
||||
q.maybe_add("maxStartTimestamp", max_start_timestamp);
|
||||
q.maybe_add("prevId", prev_id);
|
||||
q.add("order", order);
|
||||
q.add("max", max);
|
||||
|
||||
Ok(client
|
||||
.get_with::<_, Sponds>((), &q.as_slice())
|
||||
.await?
|
||||
.into_inner()
|
||||
.0)
|
||||
}
|
||||
|
||||
#[bon::builder]
|
||||
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)
|
||||
}
|
||||
|
||||
#[bon::builder]
|
||||
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)
|
||||
}
|
||||
|
||||
#[bon::builder]
|
||||
pub async fn response(
|
||||
#[builder(start_fn)] spond: Id,
|
||||
#[builder(finish_fn)] client: &RestClient,
|
||||
member: MemberId,
|
||||
accepted: bool,
|
||||
) -> Result<Responses, Error> {
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
struct Request {
|
||||
accepted: bool,
|
||||
}
|
||||
|
||||
impl RestPath<(Id, MemberId)> for Request {
|
||||
fn get_path(args: (Id, MemberId)) -> std::result::Result<String, Error> {
|
||||
let (spond, member) = args;
|
||||
Ok(format!("sponds/{spond}/responses/{member}"))
|
||||
}
|
||||
}
|
||||
|
||||
let request = Request{accepted};
|
||||
let response: restson::Response<Responses> = client.put_capture((spond, member), &request).await?;
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
41
api/src/user.rs
Normal file
41
api/src/user.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use serde::Deserialize;
|
||||
use restson::{RestClient, RestPath, Error};
|
||||
|
||||
use super::{
|
||||
UserId as Id,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct User {
|
||||
pub id: Id,
|
||||
|
||||
#[serde(flatten)]
|
||||
unknown: serde_json::Value,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for User {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", serde_json::to_string_pretty(&self.unknown).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl RestPath<()> for User {
|
||||
fn get_path(_: ()) -> std::result::Result<String, Error> {
|
||||
Ok(format!("user"))
|
||||
}
|
||||
}
|
||||
|
||||
impl RestPath<(Id, )> for User {
|
||||
fn get_path(args: (Id, )) -> std::result::Result<String, Error> {
|
||||
let (id, ) = args;
|
||||
Ok(format!("user/{id}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn identity(client: &RestClient) -> Result<User, Error> {
|
||||
Ok(client.get_with::<_, User>((), &[]).await?.into_inner())
|
||||
}
|
||||
|
||||
pub async fn with_id(client: &RestClient, id: Id) -> Result<User, Error> {
|
||||
Ok(client.get_with::<_, User>((id, ), &[]).await?.into_inner())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue