1+ use crate :: data:: current_timestamp;
2+ use crate :: logger:: LOGGER ;
3+ use serde:: { Deserialize , Serialize } ;
4+ use sqlx:: postgres:: { PgPoolOptions , PgRow } ;
5+ use sqlx:: { Executor , FromRow , Pool , Postgres , Row } ;
6+
7+ pub type BotResult < T > = Result < T , Box < dyn std:: error:: Error + Send + Sync > > ;
8+
9+ #[ derive( Debug ) ]
10+ pub struct Database {
11+ url : String ,
12+ port : u16 ,
13+ password : String ,
14+ username : String ,
15+ database : String ,
16+ pool : Option < Pool < Postgres > > ,
17+ }
18+
19+ #[ derive( Serialize , Deserialize , Debug , Clone , FromRow ) ]
20+ pub struct User {
21+ pub id : i64 ,
22+ pub chat_id : Option < i64 > ,
23+ pub name : String ,
24+ pub requests_amount : i64 ,
25+ pub timestamp : i64 ,
26+ pub register_timestamp : i64 ,
27+ }
28+
29+ pub enum UserColumn {
30+ Id ,
31+ Name ,
32+ RequestsAmount ,
33+ Timestamp ,
34+ ChatId ,
35+ RegisterTimestamp ,
36+ }
37+
38+ impl Database {
39+ pub fn new ( url : String , port : u16 , password : String , username : String , database : String ) -> Database {
40+ Database {
41+ url,
42+ port,
43+ password,
44+ username,
45+ database,
46+ pool : None ,
47+ }
48+ }
49+
50+ pub async fn try_connect ( & mut self ) {
51+
52+ LOGGER . executing ( "Connecting to postgres database..." ) ;
53+
54+ let pool = PgPoolOptions :: new ( )
55+ . max_connections ( 5 )
56+ . connect ( format ! (
57+ "postgres://{}:{}@{}:{}/{}" ,
58+ self . username,
59+ self . password,
60+ self . url,
61+ self . port,
62+ self . database,
63+ ) . as_str ( ) )
64+ . await
65+ . expect ( "Failed to connect to database \n Reason" ) ;
66+
67+ LOGGER . success ( "Connected to postgres database!" ) ;
68+
69+ self . pool = Option :: from ( pool)
70+ }
71+
72+ pub async fn init ( & self ) -> BotResult < ( ) > {
73+ if let Some ( pool) = & self . pool {
74+ sqlx:: query (
75+ r#"
76+ CREATE TABLE IF NOT EXISTS users (
77+ id BIGINT PRIMARY KEY,
78+ chat_id BIGINT,
79+ name TEXT NOT NULL,
80+ requests_amount BIGINT NOT NULL DEFAULT 0,
81+ timestamp BIGINT NOT NULL,
82+ register_timestamp BIGINT NOT NULL
83+ )
84+ "# ,
85+ )
86+ . execute ( pool)
87+ . await ?;
88+ }
89+
90+ Ok ( ( ) )
91+ }
92+
93+ pub async fn set_data (
94+ & self ,
95+ user_id : i64 ,
96+ column : UserColumn ,
97+ value : & str ,
98+ ) -> BotResult < ( ) > {
99+ let query = match column {
100+ UserColumn :: Name => "UPDATE users SET name = $1 WHERE id = $2" ,
101+ UserColumn :: RequestsAmount => "UPDATE users SET requests_amount = $1 WHERE id = $2" ,
102+ UserColumn :: Timestamp => "UPDATE users SET timestamp = $1 WHERE id = $2" ,
103+ UserColumn :: ChatId => "UPDATE users SET chat_id = $1 WHERE id = $2" ,
104+ _ => return Err ( "Cannot modify this column" . into ( ) ) ,
105+ } ;
106+
107+ let parsed_value = match column {
108+ UserColumn :: Name => value. to_string ( ) ,
109+ UserColumn :: ChatId => value. parse ( ) ?,
110+ _ => value. parse :: < i64 > ( ) ?. to_string ( ) ,
111+ } ;
112+
113+ if let Some ( pool) = & self . pool {
114+ sqlx:: query ( query)
115+ . bind ( parsed_value)
116+ . bind ( user_id)
117+ . execute ( pool)
118+ . await ?;
119+ }
120+
121+ Ok ( ( ) )
122+ }
123+
124+ pub async fn has_user ( & self , id : i64 ) -> BotResult < bool > {
125+
126+ if let Some ( pool) = & self . pool {
127+ let user = sqlx:: query ( "SELECT id FROM users WHERE id = $1" )
128+ . bind ( id)
129+ . fetch_optional ( pool)
130+ . await ?;
131+
132+ Ok ( user. is_some ( ) )
133+ } else {
134+ Ok ( false )
135+ }
136+ }
137+
138+ pub async fn add_user ( & self , user : User ) -> BotResult < ( ) > {
139+ if let Some ( pool) = & self . pool {
140+ sqlx:: query (
141+ r#"
142+ INSERT INTO users (id, chat_id, name, requests_amount, timestamp, register_timestamp)
143+ VALUES ($1, $2, $3, $4, $5, $6)
144+ "# ,
145+ )
146+ . bind ( user. id )
147+ . bind ( user. chat_id )
148+ . bind ( user. name )
149+ . bind ( user. requests_amount )
150+ . bind ( user. timestamp )
151+ . bind ( user. register_timestamp )
152+ . execute ( pool)
153+ . await ?;
154+ }
155+ Ok ( ( ) )
156+ }
157+
158+ pub async fn update_user ( & self , user : User ) -> BotResult < ( ) > {
159+ if let Some ( pool) = & self . pool {
160+ sqlx:: query (
161+ r#"
162+ UPDATE users SET
163+ chat_id = $1,
164+ name = $2,
165+ requests_amount = $3,
166+ timestamp = $4,
167+ register_timestamp = $5
168+ WHERE id = $6
169+ "# ,
170+ )
171+ . bind ( user. chat_id )
172+ . bind ( user. name )
173+ . bind ( user. requests_amount )
174+ . bind ( user. timestamp )
175+ . bind ( user. register_timestamp )
176+ . bind ( user. id )
177+ . execute ( pool)
178+ . await ?;
179+ }
180+
181+ Ok ( ( ) )
182+ }
183+
184+ pub async fn remove_user ( & self , id : i64 ) -> BotResult < ( ) > {
185+ if let Some ( pool) = & self . pool {
186+ sqlx:: query ( "DELETE FROM users WHERE id = $1" )
187+ . bind ( id)
188+ . execute ( pool)
189+ . await ?;
190+ }
191+
192+ Ok ( ( ) )
193+ }
194+
195+ pub async fn get_data ( & self , user_id : i64 , column : UserColumn ) -> BotResult < Option < String > > {
196+ if let Some ( pool) = & self . pool {
197+ let row: Option < PgRow > = sqlx:: query ( "SELECT * FROM users WHERE id = $1" )
198+ . bind ( user_id)
199+ . fetch_optional ( pool)
200+ . await ?;
201+
202+ Ok ( row. and_then ( |row| {
203+ match column {
204+ UserColumn :: Id => Some ( row. get :: < i64 , _ > ( "id" ) . to_string ( ) ) ,
205+ UserColumn :: Name => Some ( row. get :: < String , _ > ( "name" ) ) ,
206+ UserColumn :: RequestsAmount => Some ( row. get :: < i64 , _ > ( "requests_amount" ) . to_string ( ) ) ,
207+ UserColumn :: Timestamp => Some ( row. get :: < i64 , _ > ( "timestamp" ) . to_string ( ) ) ,
208+ UserColumn :: ChatId => row. get :: < Option < i64 > , _ > ( "chat_id" ) . map ( |v| v. to_string ( ) ) ,
209+ UserColumn :: RegisterTimestamp => Some ( row. get :: < i64 , _ > ( "register_timestamp" ) . to_string ( ) ) ,
210+ }
211+ } ) )
212+ } else {
213+ Ok ( None )
214+ }
215+ }
216+
217+ pub async fn get_user ( & self , id : i64 ) -> BotResult < Option < User > > {
218+ if let Some ( pool) = & self . pool {
219+ let user = sqlx:: query_as :: < _ , User > ( "SELECT * FROM users WHERE id = $1" )
220+ . bind ( id)
221+ . fetch_optional ( pool)
222+ . await ?;
223+
224+ Ok ( user)
225+ } else {
226+ Ok ( None )
227+ }
228+ }
229+
230+ pub async fn increment_requests ( & self , id : i64 ) -> BotResult < ( ) > {
231+ if let Some ( pool) = & self . pool {
232+ sqlx:: query (
233+ r#"
234+ UPDATE users SET
235+ requests_amount = requests_amount + 1,
236+ timestamp = $1
237+ WHERE id = $2
238+ "# ,
239+ )
240+ . bind ( current_timestamp ( ) )
241+ . bind ( id)
242+ . execute ( pool)
243+ . await ?;
244+ }
245+ Ok ( ( ) )
246+ }
247+
248+ }
0 commit comments