1use anyhow::Result;
7use hypercall_types::WalletAddress;
8
9use crate::{
10 CompetitionFillInput, CompetitionFillRecord, CompetitionFinalStatsInput,
11 CompetitionFinalStatsRecord, CompetitionRecord, CompetitionUpsertInput, PlatformWalletMetrics,
12 ProfileFillRecord, SymbolPnlRecord, TheoMarkRecord, WalletLedgerStats, WalletUsernameRecord,
13};
14
15#[derive(Debug)]
17pub enum CompetitionWriteError {
18 OverlapViolation,
20 UniqueViolation(String),
22 NotFound(String),
24 BadRequest(String),
26 Conflict(String),
28 Internal(anyhow::Error),
30}
31
32impl std::fmt::Display for CompetitionWriteError {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 Self::OverlapViolation => {
36 write!(f, "competition window overlaps existing competition")
37 }
38 Self::UniqueViolation(msg) => write!(f, "{msg}"),
39 Self::NotFound(msg) => write!(f, "{msg}"),
40 Self::BadRequest(msg) => write!(f, "{msg}"),
41 Self::Conflict(msg) => write!(f, "{msg}"),
42 Self::Internal(e) => write!(f, "{e}"),
43 }
44 }
45}
46
47impl std::error::Error for CompetitionWriteError {
48 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
49 match self {
50 Self::Internal(e) => Some(e.as_ref()),
51 _ => None,
52 }
53 }
54}
55
56impl From<anyhow::Error> for CompetitionWriteError {
57 fn from(e: anyhow::Error) -> Self {
58 Self::Internal(e)
59 }
60}
61
62#[async_trait::async_trait]
64pub trait CompetitionReader: Send + Sync {
65 async fn list_competitions(
67 &self,
68 state_filter: Option<&str>,
69 from_ts_ms: Option<i64>,
70 to_ts_ms: Option<i64>,
71 now_ts_ms: i64,
72 limit: i64,
73 offset: i64,
74 ) -> Result<Vec<CompetitionRecord>>;
75
76 async fn get_competition_by_id(&self, competition_id: i64)
78 -> Result<Option<CompetitionRecord>>;
79
80 async fn get_active_competition(&self, now_ts_ms: i64) -> Result<Option<CompetitionRecord>>;
82
83 async fn get_latest_completed_competition(
85 &self,
86 now_ts_ms: i64,
87 ) -> Result<Option<CompetitionRecord>>;
88
89 async fn get_competitions_to_finalize(&self, now_ts_ms: i64) -> Result<Vec<CompetitionRecord>>;
91
92 async fn get_finalized_stats(
94 &self,
95 competition_id: i64,
96 ) -> Result<Vec<CompetitionFinalStatsRecord>>;
97
98 async fn get_competition_fills_before(
101 &self,
102 cutoff_ts_ms: i64,
103 ) -> Result<Vec<CompetitionFillRecord>>;
104
105 async fn get_historical_theo_marks(
107 &self,
108 symbols: &[String],
109 cutoff_ts_ms: i64,
110 ) -> Result<Vec<TheoMarkRecord>>;
111
112 async fn get_display_usernames_batch(
114 &self,
115 wallet_strings: &[String],
116 ) -> Result<Vec<WalletUsernameRecord>>;
117
118 async fn get_display_username(&self, wallet: &WalletAddress) -> Result<Option<String>>;
120
121 async fn get_profile_image_url(&self, wallet: &WalletAddress) -> Result<Option<String>>;
123
124 async fn compute_ledger_profile_stats(
126 &self,
127 wallet: &WalletAddress,
128 now_ts_ms: i64,
129 ) -> Result<WalletLedgerStats>;
130
131 async fn get_account_first_seen_ts_ms(&self, wallet: &WalletAddress) -> Result<Option<i64>>;
133
134 async fn get_realized_pnl_by_symbol(
136 &self,
137 wallet: &WalletAddress,
138 window_start: Option<i64>,
139 window_end: Option<i64>,
140 ) -> Result<Vec<SymbolPnlRecord>>;
141
142 async fn get_profile_trade_history(
144 &self,
145 wallet: &WalletAddress,
146 window_start: Option<i64>,
147 window_end: Option<i64>,
148 from_ts_ms: Option<i64>,
149 to_ts_ms: Option<i64>,
150 symbol: Option<&str>,
151 limit: i64,
152 offset: i64,
153 ) -> Result<Vec<ProfileFillRecord>>;
154
155 async fn get_platform_wallet_metrics(&self) -> Result<Vec<PlatformWalletMetrics>>;
157}
158
159#[async_trait::async_trait]
161pub trait CompetitionWriter: CompetitionReader {
162 async fn create_competition(
164 &self,
165 input: &CompetitionUpsertInput,
166 ) -> std::result::Result<CompetitionRecord, CompetitionWriteError>;
167
168 async fn update_competition(
171 &self,
172 competition_id: i64,
173 name: &str,
174 description: Option<&str>,
175 rules_url: Option<&str>,
176 rules_content: Option<&str>,
177 win_conditions: &[String],
178 primary_win_condition: &str,
179 start_ts_ms: i64,
180 end_ts_ms: i64,
181 ) -> std::result::Result<CompetitionRecord, CompetitionWriteError>;
182
183 async fn delete_competition(&self, competition_id: i64) -> Result<usize>;
185
186 async fn record_competition_fill(&self, input: &CompetitionFillInput) -> Result<()>;
188
189 async fn finalize_competition(
193 &self,
194 competition_id: i64,
195 stats: &[CompetitionFinalStatsInput],
196 ) -> Result<usize>;
197
198 async fn set_profile_image_url(
200 &self,
201 wallet: &WalletAddress,
202 profile_image_url: &str,
203 ) -> Result<Option<String>>;
204}