Modularize, Model trait, list_owners

This commit is contained in:
2020-07-18 17:46:33 -04:00
parent 6b1f31f246
commit 9ec7fc1518
10 changed files with 462 additions and 389 deletions

46
src/models/mod.rs Normal file
View File

@@ -0,0 +1,46 @@
use serde::Deserialize;
use std::fmt;
pub mod model;
pub mod owner;
pub mod shop;
pub use model::Model;
pub use owner::Owner;
pub use shop::Shop;
#[derive(Debug, Deserialize)]
pub enum Order {
Asc,
Desc,
}
impl fmt::Display for Order {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Order::Asc => "ASC",
Order::Desc => "DESC",
}
)
}
}
#[derive(Debug, Deserialize)]
pub struct ListParams {
limit: Option<i64>,
offset: Option<i64>,
order_by: Option<String>,
order: Option<Order>,
}
impl ListParams {
pub fn get_order_by(&self) -> String {
let default_order_by = "updated_at".to_string();
let order_by = self.order_by.as_ref().unwrap_or(&default_order_by);
let order = self.order.as_ref().unwrap_or(&Order::Desc);
format!("{} {}", order_by, order)
}
}

28
src/models/model.rs Normal file
View File

@@ -0,0 +1,28 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use sqlx::postgres::PgPool;
use url::Url;
use super::ListParams;
#[async_trait]
pub trait Model
where
Self: std::marker::Sized,
{
fn resource_name() -> &'static str;
fn pk(&self) -> Option<i32>;
fn url(&self, api_url: &Url) -> Result<Url> {
if let Some(pk) = self.pk() {
Ok(api_url.join(&format!("/{}s/{}", Self::resource_name(), pk))?)
} else {
Err(anyhow!(
"Cannot get URL for {} with no primary key",
Self::resource_name()
))
}
}
async fn get(db: &PgPool, id: i32) -> Result<Self>;
async fn save(self, db: &PgPool) -> Result<Self>;
async fn list(db: &PgPool, list_params: ListParams) -> Result<Vec<Self>>;
}

81
src/models/owner.rs Normal file
View File

@@ -0,0 +1,81 @@
use anyhow::Result;
use async_trait::async_trait;
use chrono::prelude::*;
use ipnetwork::IpNetwork;
use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPool;
use uuid::Uuid;
use super::ListParams;
use super::Model;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Owner {
pub id: Option<i32>,
pub name: String,
pub api_key: Uuid,
pub ip_address: Option<IpNetwork>,
pub mod_version: String,
pub created_at: Option<NaiveDateTime>,
pub updated_at: Option<NaiveDateTime>,
}
#[async_trait]
impl Model for Owner {
fn resource_name() -> &'static str {
"owner"
}
fn pk(&self) -> Option<i32> {
self.id
}
async fn get(db: &PgPool, id: i32) -> Result<Self> {
let timer = std::time::Instant::now();
let result = sqlx::query_as!(Self, "SELECT * FROM owners WHERE id = $1", id)
.fetch_one(db)
.await?;
let elapsed = timer.elapsed();
debug!("SELECT * FROM owners ... {:.3?}", elapsed);
Ok(result)
}
async fn save(self, db: &PgPool) -> Result<Self> {
let timer = std::time::Instant::now();
let result = sqlx::query_as!(
Self,
"INSERT INTO owners
(name, api_key, ip_address, mod_version, created_at, updated_at)
VALUES ($1, $2, $3, $4, now(), now())
RETURNING *",
self.name,
self.api_key,
self.ip_address,
self.mod_version,
)
.fetch_one(db)
.await?;
let elapsed = timer.elapsed();
debug!("INSERT INTO owners ... {:.3?}", elapsed);
Ok(result)
}
async fn list(db: &PgPool, list_params: ListParams) -> Result<Vec<Self>> {
let timer = std::time::Instant::now();
let result = sqlx::query_as!(
Self,
"SELECT * FROM owners
ORDER BY $1
LIMIT $2
OFFSET $3",
list_params.get_order_by(),
list_params.limit.unwrap_or(10),
list_params.offset.unwrap_or(0),
)
.fetch_all(db)
.await?;
let elapsed = timer.elapsed();
debug!("SELECT * FROM owners ... {:.3?}", elapsed);
Ok(result)
}
}

86
src/models/shop.rs Normal file
View File

@@ -0,0 +1,86 @@
use anyhow::Result;
use async_trait::async_trait;
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPool;
use super::ListParams;
use super::Model;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Shop {
pub id: Option<i32>,
pub name: String,
pub owner_id: i32,
pub description: String,
pub is_not_sell_buy: bool,
pub sell_buy_list_id: i32,
pub vendor_id: i32,
pub vendor_gold: i32,
pub created_at: Option<NaiveDateTime>,
pub updated_at: Option<NaiveDateTime>,
}
#[async_trait]
impl Model for Shop {
fn resource_name() -> &'static str {
"shop"
}
fn pk(&self) -> Option<i32> {
self.id
}
async fn get(db: &PgPool, id: i32) -> Result<Self> {
let timer = std::time::Instant::now();
let result = sqlx::query_as!(Self, "SELECT * FROM shops WHERE id = $1", id)
.fetch_one(db)
.await?;
let elapsed = timer.elapsed();
debug!("SELECT * FROM shops ... {:.3?}", elapsed);
Ok(result)
}
async fn save(self, db: &PgPool) -> Result<Self> {
let timer = std::time::Instant::now();
let result = sqlx::query_as!(
Self,
"INSERT INTO shops
(name, owner_id, description, is_not_sell_buy, sell_buy_list_id, vendor_id,
vendor_gold, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, now(), now())
RETURNING *",
self.name,
self.owner_id,
self.description,
self.is_not_sell_buy,
self.sell_buy_list_id,
self.vendor_id,
self.vendor_gold,
)
.fetch_one(db)
.await?;
let elapsed = timer.elapsed();
debug!("INSERT INTO shops ... {:.3?}", elapsed);
Ok(result)
}
async fn list(db: &PgPool, list_params: ListParams) -> Result<Vec<Self>> {
let timer = std::time::Instant::now();
let result = sqlx::query_as!(
Self,
"SELECT * FROM shops
ORDER BY $1
LIMIT $2
OFFSET $3",
list_params.get_order_by(),
list_params.limit.unwrap_or(10),
list_params.offset.unwrap_or(0),
)
.fetch_all(db)
.await?;
let elapsed = timer.elapsed();
debug!("SELECT * FROM shops ... {:.3?}", elapsed);
Ok(result)
}
}