Added arrangement, more property fields, and impls

This commit is contained in:
Franklin 2023-05-08 12:46:26 -04:00
parent 1b4600ec21
commit 8de10c4e3b
12 changed files with 234 additions and 26 deletions

View File

@ -0,0 +1,56 @@
use std::{fmt::Display, str::FromStr};
use sqlx::{Postgres, postgres::{PgArgumentBuffer, PgValueRef, PgTypeInfo}, encode::IsNull, error::BoxDynError};
use crate::domain::error::FromStrError;
use super::ArrangementType;
impl Display for ArrangementType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ArrangementType::Rent => write!(f, "Rent"),
ArrangementType::Sale => write!(f, "Sale"),
}
}
}
impl FromStr for ArrangementType {
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Rent" => Ok(Self::Rent),
"Sale" => Ok(Self::Sale),
_ => Err(FromStrError)
}
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Encode<'_, Postgres> for ArrangementType {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
let binding = serde_json::to_string(self).unwrap();
<&str as sqlx::Encode<Postgres>>::encode(&binding, buf)
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Decode<'_, Postgres> for ArrangementType {
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
let column = value.as_str()?;
match serde_json::from_str(column) {
Ok(listing_state) => Ok(listing_state),
Err(error) => Err(Box::new(error)),
}
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Type<Postgres> for ArrangementType {
fn type_info() -> PgTypeInfo {
PgTypeInfo::with_name("VARCHAR")
}
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
*ty == Self::type_info()
}
}

View File

@ -0,0 +1,10 @@
use serde::{Serialize, Deserialize};
pub mod impls;
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ArrangementType {
#[default]
Rent,
Sale,
}

View File

@ -0,0 +1,61 @@
use std::{fmt::Display, str::FromStr};
use sqlx::{Postgres, postgres::{PgArgumentBuffer, PgValueRef, PgTypeInfo}, encode::IsNull, error::BoxDynError};
use crate::domain::error::FromStrError;
use super::FrameFormat;
impl Display for FrameFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FrameFormat::Landscape => write!(f, "Landscape"),
FrameFormat::Instagram => write!(f, "Instagram"),
FrameFormat::Portrait => write!(f, "Portrait"),
FrameFormat::Square => write!(f, "Square")
}
}
}
impl FromStr for FrameFormat {
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Landscape" => Ok(Self::Landscape),
"Instagram" => Ok(Self::Instagram),
"Portrait" => Ok(Self::Portrait),
"Square" => Ok(Self::Square),
_ => Err(FromStrError)
}
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Encode<'_, Postgres> for FrameFormat {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
let binding = self.to_string();
<&str as sqlx::Encode<Postgres>>::encode(&binding, buf)
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Decode<'_, Postgres> for FrameFormat {
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
let column = value.as_str()?;
match Self::from_str(column) {
Ok(listing_state) => Ok(listing_state),
Err(error) => Err(Box::new(error)),
}
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Type<Postgres> for FrameFormat {
fn type_info() -> PgTypeInfo {
PgTypeInfo::with_name("VARCHAR")
}
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
*ty == Self::type_info()
}
}

38
src/domain/format/mod.rs Normal file
View File

@ -0,0 +1,38 @@
pub mod impls;
use serde::{Serialize, Deserialize};
/// Allows realtors to pick the size of each property to fit the carroussel.
/// This is easy for agents to use and is better than having them pick what size everything should be.
/// All sizes are in w x h in squares
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FrameFormat {
/// 2x1
#[default]
Landscape,
/// 2x2
Instagram,
/// 1x2
Portrait,
// 1x1
Square,
}
impl FrameFormat {
pub fn get_width(&self) -> usize {
match self {
FrameFormat::Landscape => 2,
FrameFormat::Instagram => 2,
FrameFormat::Portrait => 1,
FrameFormat::Square => 1,
}
}
pub fn get_height(&self) -> usize {
match self {
FrameFormat::Landscape => 1,
FrameFormat::Instagram => 2,
FrameFormat::Portrait => 2,
FrameFormat::Square => 1,
}
}
}

View File

@ -11,6 +11,9 @@ pub mod realtor;
pub mod thing_pk;
pub mod trackable;
pub mod view;
pub mod format;
pub mod price;
pub mod arrangement;
pub mod project_type;
pub mod error;
pub mod project_type;

16
src/domain/price.rs Normal file
View File

@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::arrangement::ArrangementType;
/// This is what lets the backend know what a property sells for/rents for.
/// Store at least one of these per Property.
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, PartialOrd)]
pub struct PropertyPrice {
#[serde(rename = "propertyId")]
pub property_id: Uuid,
pub price: f64,
pub currency: String,
pub arrangement: ArrangementType,
}

View File

@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::{media::MediaList, property_sale_type::PropertySaleType, property_type::PropertyType};
use super::{media::MediaList, property_sale_type::PropertySaleType, property_type::PropertyType, format::FrameFormat};
/// A property can belong to a project, or not. It should always belong to a realtor.
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, PartialOrd)]
@ -17,20 +17,22 @@ pub struct Property {
pub property_type: PropertyType,
#[serde(rename = "propertySaleType")]
pub property_sale_type: PropertySaleType,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub district: Option<String>,
#[serde(rename = "priceUsd")]
pub price_usd: f64,
pub country: String,
pub city: String,
pub district: String,
/// This gives the realtor the option to order the projects/properties
#[serde(rename = "orderIndex")]
pub order_index: i32,
#[serde(rename = "thumbnailFormat")]
pub thumbnail_format: FrameFormat,
/// Amount of rooms in unit
pub rooms: i16,
/// Amount of bathrooms in unit
pub bathrooms: f32,
/// In meters squared
pub area: f32,
#[serde(rename = "parkingSpots")]
pub parking_spots: i16,
#[serde(rename = "adminTag")]
pub admin_tag: Option<String>,
#[serde(rename = "timeCreated")]

View File

@ -54,7 +54,7 @@ impl sqlx::Decode<'_, Postgres> for ThingPk {
#[cfg(feature = "sqlx")]
impl sqlx::Type<Postgres> for ThingPk {
fn type_info() -> PgTypeInfo {
PgTypeInfo::with_name("TEXT")
PgTypeInfo::with_name("VARCHAR")
}
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {

17
src/dto/filter.rs Normal file
View File

@ -0,0 +1,17 @@
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub enum Filter {
Country(String),
City(String),
District(String),
PriceGreaterThan(f64),
PriceLessThan(f64),
Rooms(i32),
Bathrooms(i32),
}

View File

@ -1 +1,2 @@
pub mod payloads;
pub mod filter;

View File

@ -2,10 +2,8 @@ use chrono::{NaiveDate, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::domain::{
media::MediaList, project::Project, project_condition::ProjectCondition,
project_state::ProjectState, project_type::ProjectType,
};
use crate::domain::{project_type::ProjectType, project_state::ProjectState, project_condition::ProjectCondition, media::MediaList, project::Project};
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ProjectForCreationPayload {
@ -32,6 +30,8 @@ pub struct ProjectForCreationPayload {
#[serde(rename = "orderIndex")]
pub order_index: i32,
}
impl From<ProjectForCreationPayload> for Project {
fn from(value: ProjectForCreationPayload) -> Self {
Self {
@ -54,4 +54,4 @@ impl From<ProjectForCreationPayload> for Project {
last_updated: Utc::now(),
}
}
}
}

View File

@ -4,7 +4,7 @@ use uuid::Uuid;
use crate::domain::{
media::MediaList, property::Property, property_sale_type::PropertySaleType,
property_type::PropertyType,
property_type::PropertyType, format::FrameFormat,
};
/// A property can belong to a project, or not. It should always belong to a realtor.
@ -19,20 +19,22 @@ pub struct PropertyForCreationPayload {
pub property_type: PropertyType,
#[serde(rename = "propertySaleType")]
pub property_sale_type: PropertySaleType,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub district: Option<String>,
#[serde(rename = "priceUsd")]
pub price_usd: f64,
pub country: String,
pub city: String,
pub district: String,
/// This gives the realtor the option to order the projects/properties. On birth,
#[serde(rename = "orderIndex")]
pub order_index: i32,
#[serde(rename = "thumbnailFormat")]
pub thumbnail_format: FrameFormat,
/// Amount of rooms in unit
pub rooms: i16,
/// Amount of bathrooms in unit
pub bathrooms: f32,
/// In meters squared
pub area: f32,
#[serde(rename = "parkingSpots")]
pub parking_spots: i16,
#[serde(rename = "adminTag")]
pub admin_tag: Option<String>,
}
@ -49,10 +51,12 @@ impl From<PropertyForCreationPayload> for Property {
country: value.country,
city: value.city,
district: value.district,
price_usd: value.price_usd,
order_index: value.order_index,
thumbnail_format: value.thumbnail_format,
rooms: value.rooms,
bathrooms: value.bathrooms,
area: value.area,
parking_spots: value.parking_spots,
admin_tag: value.admin_tag,
time_created: Utc::now(),
last_updated: Utc::now(),