tpex_api/
shared.rs

1use std::{fmt::Display, str::FromStr};
2
3use num_traits::FromPrimitive;
4use serde::{de::Visitor, Deserialize, Serialize};
5use tpex::PlayerId;
6use base64::prelude::*;
7
8#[repr(u8)]
9#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)]
10#[derive(num_derive::FromPrimitive)]
11pub enum TokenLevel  {
12    /// The client can only get general pricing data
13    ReadOnly = 0,
14    /// The client can act on behalf of a user, but not for banker commands
15    ProxyOne = 1,
16    /// The client can act on behalf of any user, and perform admin commands
17    ProxyAll = 2,
18}
19impl Serialize for TokenLevel {
20    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
21    where S: serde::Serializer {
22        serializer.serialize_u64(*self as u64)
23    }
24}
25impl<'de> Deserialize<'de> for TokenLevel {
26    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
27    where D: serde::Deserializer<'de> {
28        struct Inner;
29        impl Visitor<'_> for Inner {
30            type Value = TokenLevel;
31
32            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
33                write!(formatter, "an integer TokenLevel")
34            }
35
36            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
37            where E: serde::de::Error, {
38                TokenLevel::from_u64(v).ok_or(E::invalid_value(serde::de::Unexpected::Unsigned(v), &Self))
39            }
40        }
41        deserializer.deserialize_u64(Inner)
42    }
43}
44
45#[derive(PartialEq, Eq, Debug, Clone, Copy)]
46pub struct Token(pub [u8;16]);
47impl Token {
48    #[cfg(feature = "server")]
49    pub fn generate() -> Token {
50        let mut ret = Token(Default::default());
51        getrandom::fill(&mut ret.0).expect("Could not generate token");
52        ret
53    }
54}
55
56impl FromStr for Token {
57    type Err = base64::DecodeSliceError;
58
59    fn from_str(s: &str) -> Result<Self, Self::Err> {
60        let mut ret = Token(Default::default());
61        let len = BASE64_STANDARD_NO_PAD.decode_slice(s, &mut ret.0)?;
62        if len != ret.0.len() {
63            // FIXME: better error here
64            Err(base64::DecodeSliceError::OutputSliceTooSmall)
65        }
66        else {
67            Ok(ret)
68        }
69    }
70}
71impl Display for Token {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        write!(f, "{}", BASE64_STANDARD_NO_PAD.encode(self.0))
74    }
75}
76impl Serialize for Token {
77    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
78    where
79        S: serde::Serializer {
80        serializer.serialize_str(&self.to_string())
81    }
82}
83impl<'de> Deserialize<'de> for Token {
84    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
85    where D: serde::Deserializer<'de> {
86        struct Inner;
87        impl Visitor<'_> for Inner {
88            type Value = Token;
89
90            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
91                write!(formatter, "a base64-encoded token")
92            }
93
94            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
95                where
96                    E: serde::de::Error, {
97                v.parse().map_err(E::custom)
98            }
99        }
100        deserializer.deserialize_str(Inner)
101    }
102}
103
104#[derive(PartialEq, Eq, Debug, Clone)]
105#[derive(serde::Serialize, serde::Deserialize)]
106pub struct TokenInfo {
107    pub token: Token,
108    pub user: PlayerId,
109    pub level: TokenLevel
110}
111
112#[derive(Debug, Clone)]
113#[derive(serde::Serialize, serde::Deserialize)]
114pub struct TokenPostArgs {
115    pub level: TokenLevel,
116    pub user: PlayerId
117}
118
119#[derive(Default, Debug, Clone)]
120#[derive(serde::Serialize, serde::Deserialize)]
121pub struct TokenDeleteArgs {
122    pub token: Option<Token>
123}
124
125#[derive(Default, Debug, Clone)]
126#[derive(serde::Serialize, serde::Deserialize)]
127pub struct StateGetArgs {
128    pub from: Option<u64>
129}
130
131#[derive(Default, Debug, Clone)]
132#[derive(serde::Serialize, serde::Deserialize)]
133pub struct StatePatchArgs {
134    pub id: Option<u64>
135}
136#[derive(Default, Debug, Clone)]
137#[derive(serde::Serialize, serde::Deserialize)]
138pub struct ErrorInfo {
139    pub error: String
140}
141#[derive(Default, Debug, Clone)]
142#[derive(serde::Serialize, serde::Deserialize)]
143pub struct InspectBalanceGetArgs {
144    pub player: PlayerId
145}
146#[derive(Default, Debug, Clone)]
147#[derive(serde::Serialize, serde::Deserialize)]
148pub struct InspectAssetsGetArgs {
149    pub player: PlayerId
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
153pub enum PriceChangeCause {
154    Buy,
155    Sell,
156    Cancel
157}
158
159#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
160pub struct PriceSummary {
161    pub time: chrono::DateTime<chrono::Utc>,
162    pub best_buy: Option<tpex::Coins>,
163    pub n_buy: u64,
164    pub best_sell: Option<tpex::Coins>,
165    pub n_sell: u64
166}
167impl PriceSummary {
168    #[allow(unused)]
169    pub const fn mid_market(&self) -> Option<tpex::Coins> {
170        match (self.best_buy, self.best_sell) {
171            (Some(best_buy), Some(best_sell)) => Some(tpex::Coins::from_millicoins(best_buy.millicoins().saturating_add(best_sell.millicoins()) / 2)),
172            (None, Some(x)) |
173            (Some(x), None) => Some(x),
174            (None, None) => None
175        }
176    }
177    #[allow(unused)]
178    pub fn cause(&self, prev: &PriceSummary) -> PriceChangeCause {
179        match (self.n_buy > prev.n_buy, self.n_sell > prev.n_sell) {
180            // There's no way for both to increase, so we were not given the previous order
181            (true, true) => panic!("Passed invalid previous value to tpex_api::PriceSummary::cause"),
182            // If neither have increased, it was a cancel
183            (false, false) => PriceChangeCause::Cancel,
184            (true , false) => PriceChangeCause::Buy,
185            (false, true ) => PriceChangeCause::Sell
186        }
187    }
188    #[allow(unused)]
189    // Only checks to see if the actual price change is the same, not the time it took place
190    pub fn same_prices_as(&self, other: &PriceSummary) -> bool {
191        self.best_buy == other.best_buy && self.n_buy == other.n_buy && self.best_sell == other.best_sell && self.n_sell == other.n_sell
192    }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct PriceHistoryArgs {
197    pub asset: tpex::AssetId,
198}