use std::str::FromStr; use crate::db; use crate::error_handler::ServiceError; use crate::db::schema::airports; use diesel::prelude::*; use postgis_diesel::types::*; use postgis_diesel::functions::*; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, AsChangeset, Insertable)] #[diesel(table_name = airports)] pub struct InsertAirport { pub icao: String, pub category: String, pub full_name: String, pub elevation_ft: Option, pub continent: String, pub iso_country: String, pub iso_region: String, pub municipality: String, pub gps_code: String, pub iata_code: String, pub local_code: String, pub point: Point } #[derive(Debug)] pub struct QueryFilters { pub name: Option, pub icao: Option, pub bounds: Option>, pub category: Option, pub order_field: Option, pub order_by: Option } impl Default for QueryFilters { fn default() -> Self { QueryFilters { name: None, icao: None, bounds: None, category: None, order_field: None, order_by: None } } } #[derive(Debug)] pub enum QueryOrderBy { Asc, Desc } impl FromStr for QueryOrderBy { type Err = (); fn from_str(s: &str) -> Result { match s { "asc" => Ok(QueryOrderBy::Asc), "desc" => Ok(QueryOrderBy::Desc), _ => Err(()) } } } #[derive(Debug)] pub enum QueryOrderField { Icao, Name, Category, Continent, Country, Region, Municipality, GPS, Iata, Local, } impl FromStr for QueryOrderField { type Err = (); fn from_str(s: &str) -> Result { match s { "icao" => Ok(QueryOrderField::Icao), "name" => Ok(QueryOrderField::Name), "category" => Ok(QueryOrderField::Category), "continent" => Ok(QueryOrderField::Continent), "iso_country" => Ok(QueryOrderField::Country), "iso_region" => Ok(QueryOrderField::Region), "municipality" => Ok(QueryOrderField::Municipality), "gps_code" => Ok(QueryOrderField::GPS), "iata_code" => Ok(QueryOrderField::Iata), "local_code" => Ok(QueryOrderField::Local), _ => Err(()) } } } #[derive(Serialize, Deserialize, Queryable, QueryableByName)] #[diesel(table_name = airports)] pub struct QueryAirport { pub icao: String, pub id: i32, pub category: String, pub full_name: String, pub elevation_ft: Option, pub continent: String, pub iso_country: String, pub iso_region: String, pub municipality: String, pub gps_code: String, pub iata_code: String, pub local_code: String, pub point: Point } impl QueryAirport { pub fn get_all(filters: &QueryFilters, limit: i32, page: i32) -> Result, ServiceError> { let mut conn = db::connection()?; let mut query = airports::table.limit(limit as i64).into_boxed(); // Limit query to page and limit let offset = (page - 1) * limit; query = query.offset(offset as i64); if let Some(bounds) = &filters.bounds { query = query.filter(st_contains(bounds, airports::point)); } if let Some(category) = &filters.category { query = query.filter(airports::category.eq(category)); } if let Some(icao) = &filters.icao { if let Some(name) = &filters.name { query = query.filter( airports::icao.ilike(format!("%{}%", icao)).or( airports::full_name.ilike(format!("%{}%", name)) ) ) } else { query = query.filter(airports::icao.ilike(format!("%{}%", icao))) } } if let Some(order_by) = &filters.order_by { match order_by { QueryOrderBy::Asc => { if let Some(order_field) = &filters.order_field { query = match order_field { QueryOrderField::Icao => query.order(airports::icao.asc()), QueryOrderField::Name => query.order(airports::full_name.asc()), QueryOrderField::Category => query.order(airports::category.asc()), QueryOrderField::Continent => query.order(airports::continent.asc()), QueryOrderField::Country => query.order(airports::iso_country.asc()), QueryOrderField::Region => query.order(airports::iso_region.asc()), QueryOrderField::Municipality => query.order(airports::municipality.asc()), QueryOrderField::GPS => query.order(airports::gps_code.asc()), QueryOrderField::Iata => query.order(airports::iata_code.asc()), QueryOrderField::Local => query.order(airports::local_code.asc()), }; }; }, QueryOrderBy::Desc => { if let Some(order_field) = &filters.order_field { query = match order_field { QueryOrderField::Icao => query.order(airports::icao.desc()), QueryOrderField::Name => query.order(airports::full_name.desc()), QueryOrderField::Category => query.order(airports::category.desc()), QueryOrderField::Continent => query.order(airports::continent.desc()), QueryOrderField::Country => query.order(airports::iso_country.desc()), QueryOrderField::Region => query.order(airports::iso_region.desc()), QueryOrderField::Municipality => query.order(airports::municipality.desc()), QueryOrderField::GPS => query.order(airports::gps_code.desc()), QueryOrderField::Iata => query.order(airports::iata_code.desc()), QueryOrderField::Local => query.order(airports::local_code.desc()), }; }; } } } let airports: Vec = query.load::(&mut conn)?; Ok(airports) } pub fn get_count(filters: &QueryFilters) -> Result { let mut conn = db::connection()?; let mut query = airports::table.count().into_boxed(); if let Some(bounds) = &filters.bounds { query = query.filter(st_contains(bounds, airports::point)); } if let Some(category) = &filters.category { query = query.filter(airports::category.eq(category)); } if let Some(icao) = &filters.icao { if let Some(name) = &filters.name { query = query.filter( airports::icao.ilike(format!("%{}%", icao)).or( airports::full_name.ilike(format!("%{}%", name)) ) ) } else { query = query.filter(airports::icao.ilike(format!("%{}%", icao))) } } let count: i64 = query.get_result(&mut conn)?; return Ok(count); } pub fn find(icao: String) -> Result { let mut conn = db::connection()?; let airport = airports::table.filter(airports::icao.eq(icao)).first(&mut conn)?; Ok(airport) } pub fn create(airport: InsertAirport) -> Result { let mut conn = db::connection()?; let airport = InsertAirport::from(airport); let airport = diesel::insert_into(airports::table) .values(airport) .get_result(&mut conn)?; Ok(airport) } pub fn update(id: i32, airport: InsertAirport) -> Result { let mut conn = db::connection()?; let airport = diesel::update(airports::table) .filter(airports::id.eq(id)) .set(airport) .get_result(&mut conn)?; Ok(airport) } pub fn delete(id: Option) -> Result { let mut conn = db::connection()?; let res = match id { Some(id) => diesel::delete(airports::table.filter(airports::id.eq(id))).execute(&mut conn)?, None => diesel::delete(airports::table).execute(&mut conn)? }; Ok(res) } }