use tokio_postgres::Client as PgClient; use std::sync::Arc; use crate::models::car::{BindingCar, Car, Report}; use crate::storages::traits::CarRepository; use async_trait::async_trait; pub struct PostgresCarRepository { pub connection: Arc } #[async_trait] impl CarRepository for PostgresCarRepository { async fn create(&self, car: BindingCar) -> Result { let result = self.connection.query( "INSERT INTO Car(brand, model, price, owner_id, car_station_id) \ VALUES ($1, $2, $3, $4, $5) RETURNING *", &[&car.brand, &car.model, &car.price, &car.owner_id.parse::().unwrap(), &car.car_station_id.parse::().unwrap()] ).await; if let Ok(rows) = result { let row = rows.get(0).unwrap(); Ok(Car { id: row.get::<&str, i32>("id").to_string(), brand: row.get("brand"), model: row.get("model"), price: row.get("price"), owner_id: row.get::<&str, i32>("owner_id").to_string(), car_station_id: row.get::<&str, i32>("car_station_id").to_string() }) } else { Err(result.unwrap_err().to_string()) } } async fn read(&self, id: String) -> Result { let result = self.connection.query( "SELECT * FROM Car WHERE id = $1", &[&id.parse::().unwrap()] ).await; if let Ok(rows) = result { let row = rows.get(0).ok_or("Car not found".to_owned())?; Ok(Car { id: row.get::<&str, i32>("id").to_string(), brand: row.get("brand"), model: row.get("model"), price: row.get("price"), owner_id: row.get::<&str, i32>("owner_id").to_string(), car_station_id: row.get::<&str, i32>("car_station_id").to_string() }) } else { Err(result.unwrap_err().to_string()) } } async fn read_all(&self) -> Result, String> { let result = self.connection.query( "SELECT * FROM Car", &[] ).await; if let Ok(rows) = result { Ok( rows.into_iter().map(|r| Car { id: r.get::<&str, i32>("id").to_string(), brand: r.get("brand"), model: r.get("model"), price: r.get("price"), owner_id: r.get::<&str, i32>("owner_id").to_string(), car_station_id: r.get::<&str, i32>("car_station_id").to_string() }).collect() ) } else { Err(result.unwrap_err().to_string()) } } async fn update(&self, id: String, car: BindingCar) -> Result { let result = self.connection.query( "UPDATE Car SET brand = $1, model = $2, price = $3, owner_id = $4, car_station_id = $5 WHERE id = $6 RETURNING *", &[&car.brand, &car.model, &car.price, &car.owner_id.parse::().unwrap(), &car.car_station_id.parse::().unwrap(), &id.parse::().unwrap()] ).await; if let Ok(rows) = &result { let row = rows.get(0).unwrap(); Ok(Car { id: row.get::<&str, i32>("id").to_string(), brand: row.get("brand"), model: row.get("model"), price: row.get("price"), owner_id: row.get::<&str, i32>("owner_id").to_string(), car_station_id: row.get::<&str, i32>("car_station_id").to_string() }) } else { Err(result.unwrap_err().to_string()) } } async fn delete(&self, id: String) -> Result<(), String> { let result = self.connection.execute( "DELETE FROM Car WHERE id = $1", &[&id.parse::().unwrap()] ).await; if let Ok(rows) = result { if rows == 0 { Err("Car not found".to_owned()) } else { Ok(()) } } else { Err(result.unwrap_err().to_string()) } } async fn get_report(&self) -> Result, String> { let result = self.connection.query( "SELECT c.id AS \"car_id\", c.brand AS \"brand\", c.model AS \"model\", COUNT(r.id) AS \"times\", ROUND(SUM(c.price * r.time_amount), 2) AS \"income\" FROM car c JOIN rent r ON c.id = r.car_id WHERE date_trunc('month', r.start_time) = date_trunc('month', CURRENT_DATE) GROUP BY c.id;", &[] ).await; if let Ok(rows) = result { Ok( rows.into_iter() .map(|row| Report { car_id: row.get::<&str, i32>("car_id").to_string(), brand: row.get("brand"), model: row.get("model"), times: row.get("times"), income: row.get("income") }) .collect() ) } else { Err(result.unwrap_err().to_string()) } } }