First functional commit
This commit is contained in:
commit
54a576e853
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
1504
Cargo.lock
generated
Normal file
1504
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "remax-types"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.23", features = [ "serde" ] }
|
||||
chrono-tz = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
uuid = { version = "1.3.0", features = ["serde"] }
|
||||
format_num = "0.1.0"
|
||||
sqlx = { version = "0.6.0", features = [ "runtime-tokio-rustls", "postgres", "chrono", "uuid" ] }
|
||||
bincode = "1.3.3"
|
15
src/domain/agent/mod.rs
Normal file
15
src/domain/agent/mod.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Agent {
|
||||
pub id: Uuid,
|
||||
#[serde(rename = "fullName")]
|
||||
pub full_name: String,
|
||||
#[serde(rename = "timeCreated")]
|
||||
pub time_created: DateTime<Utc>,
|
||||
#[serde(rename = "lastUpdated")]
|
||||
pub last_updated: DateTime<Utc>,
|
||||
}
|
19
src/domain/contact_info/mod.rs
Normal file
19
src/domain/contact_info/mod.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ContactInformation {
|
||||
#[serde(rename = "agentId")]
|
||||
pub agent_id: Uuid,
|
||||
#[serde(rename = "phoneNumber")]
|
||||
pub phone_number: String,
|
||||
pub email: String,
|
||||
#[serde(rename = "profilePictureUrl")]
|
||||
pub profile_picture_url: Option<String>,
|
||||
/// This is the message to be sent to this user on every platform. So for whatsapp
|
||||
/// it will be the wa.me link message that gets placed in the chat when a user clicks on the link.
|
||||
#[serde(rename = "defaultMessage")]
|
||||
pub default_message: String,
|
||||
}
|
1
src/domain/filters/mod.rs
Normal file
1
src/domain/filters/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod property;
|
24
src/domain/filters/property.rs
Normal file
24
src/domain/filters/property.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum PropertyFilter {
|
||||
// By Time
|
||||
Before(DateTime<Utc>),
|
||||
After(DateTime<Utc>),
|
||||
|
||||
// By Word matching
|
||||
Title(String),
|
||||
|
||||
// By cost
|
||||
CheaperThan(u64),
|
||||
MoreExpensiveThan(u64),
|
||||
|
||||
// By Contract type
|
||||
ContractType(String),
|
||||
|
||||
// By Size
|
||||
BiggerOrEqualTo(u32),
|
||||
SmallerOrEqualTo(u32),
|
||||
}
|
14
src/domain/location/mod.rs
Normal file
14
src/domain/location/mod.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Location {
|
||||
pub id: Uuid,
|
||||
pub country: String,
|
||||
pub province: String,
|
||||
pub city: String,
|
||||
pub district: Option<String>,
|
||||
#[serde(rename = "googleMapsUrl")]
|
||||
pub google_maps_url: Option<String>,
|
||||
}
|
7
src/domain/mod.rs
Normal file
7
src/domain/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub mod agent;
|
||||
pub mod property;
|
||||
pub mod location;
|
||||
pub mod contact_info;
|
||||
pub mod property_details;
|
||||
pub mod filters;
|
||||
pub mod money;
|
85
src/domain/money/mod.rs
Normal file
85
src/domain/money/mod.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use format_num::NumberFormat;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sqlx::{Postgres, postgres::{PgArgumentBuffer, PgValueRef, PgTypeInfo}, encode::IsNull, error::BoxDynError};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Currency {
|
||||
#[default]
|
||||
Usd,
|
||||
Dop,
|
||||
Eur,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Money {
|
||||
pub amount: f64,
|
||||
pub currency: Currency,
|
||||
}
|
||||
|
||||
impl Display for Money {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let number_formatter = NumberFormat::new();
|
||||
let amount_fmted = number_formatter.format(".2f", self.amount);
|
||||
match self.currency {
|
||||
Currency::Usd => write!(f, "US${}", amount_fmted),
|
||||
Currency::Dop => write!(f, "RD${}", amount_fmted),
|
||||
Currency::Eur => write!(f, "EUR€{}", amount_fmted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Encode<'_, Postgres> for Currency {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let encoded_value = bincode::serialize(self).unwrap();
|
||||
<Vec<u8> as sqlx::Encode<Postgres>>::encode(encoded_value, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Decode<'_, Postgres> for Currency {
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
let column = value.as_bytes()?;
|
||||
match bincode::deserialize(column) {
|
||||
Ok(listing_state) => Ok(listing_state),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Type<Postgres> for Currency {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::with_name("bytea")
|
||||
}
|
||||
|
||||
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
|
||||
*ty == Self::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Encode<'_, Postgres> for Money {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let encoded_value = bincode::serialize(self).unwrap();
|
||||
<Vec<u8> as sqlx::Encode<Postgres>>::encode(encoded_value, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Decode<'_, Postgres> for Money {
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
let column = value.as_bytes()?;
|
||||
match bincode::deserialize(column) {
|
||||
Ok(listing_state) => Ok(listing_state),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Type<Postgres> for Money {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::with_name("bytea")
|
||||
}
|
||||
|
||||
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
|
||||
*ty == Self::type_info()
|
||||
}
|
||||
}
|
57
src/domain/property/mod.rs
Normal file
57
src/domain/property/mod.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sqlx::{Postgres, postgres::{PgValueRef, PgTypeInfo, PgArgumentBuffer}, error::BoxDynError, encode::IsNull};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct Property {
|
||||
pub id: Uuid,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
#[serde(rename = "agentId")]
|
||||
pub agent_id: Uuid,
|
||||
pub state: ListingState,
|
||||
#[serde(rename = "timeCreated")]
|
||||
pub time_created: DateTime<Utc>,
|
||||
#[serde(rename = "lastUpdated")]
|
||||
pub last_updated: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub enum ListingState {
|
||||
#[default]
|
||||
Draft,
|
||||
Archived,
|
||||
Sold,
|
||||
Listed,
|
||||
Deleted,
|
||||
}
|
||||
|
||||
|
||||
impl sqlx::Encode<'_, Postgres> for ListingState {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let encoded_value = bincode::serialize(self).unwrap();
|
||||
<Vec<u8> as sqlx::Encode<Postgres>>::encode(encoded_value, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Decode<'_, Postgres> for ListingState {
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
let column = value.as_bytes()?;
|
||||
match bincode::deserialize(column) {
|
||||
Ok(listing_state) => Ok(listing_state),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Type<Postgres> for ListingState {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::with_name("bytea")
|
||||
}
|
||||
|
||||
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
|
||||
*ty == Self::type_info()
|
||||
}
|
||||
}
|
95
src/domain/property_details/mod.rs
Normal file
95
src/domain/property_details/mod.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sqlx::{Postgres, postgres::{PgArgumentBuffer, PgValueRef, PgTypeInfo}, encode::IsNull, error::BoxDynError};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::money::Money;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd, Default)]
|
||||
pub struct PropertyDetails {
|
||||
#[serde(rename = "propertyId")]
|
||||
pub property_id: Uuid,
|
||||
pub meters: f32,
|
||||
#[serde(rename = "listingType")]
|
||||
pub listing_type: ListingType,
|
||||
#[serde(rename = "photoUrls")]
|
||||
pub photo_urls: Photos,
|
||||
#[serde(rename = "locationId")]
|
||||
pub location_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum ListingType {
|
||||
Rent(Money),
|
||||
Sale(Money),
|
||||
/// Sale first, Rent Second
|
||||
Both(Money, Money),
|
||||
}
|
||||
impl Default for ListingType {
|
||||
fn default() -> Self {
|
||||
Self::Sale(Money::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd, Default)]
|
||||
pub struct Photos {
|
||||
pub photos: Vec<String>
|
||||
}
|
||||
|
||||
|
||||
// Database implementations
|
||||
|
||||
impl sqlx::Encode<'_, Postgres> for Photos {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let encoded_value = bincode::serialize(self).unwrap();
|
||||
<Vec<u8> as sqlx::Encode<Postgres>>::encode(encoded_value, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Decode<'_, Postgres> for Photos {
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
let column = value.as_bytes()?;
|
||||
match bincode::deserialize(column) {
|
||||
Ok(listing_state) => Ok(listing_state),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Type<Postgres> for Photos {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::with_name("bytea")
|
||||
}
|
||||
|
||||
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
|
||||
*ty == Self::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl sqlx::Encode<'_, Postgres> for ListingType {
|
||||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
|
||||
let encoded_value = bincode::serialize(self).unwrap();
|
||||
<Vec<u8> as sqlx::Encode<Postgres>>::encode(encoded_value, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Decode<'_, Postgres> for ListingType {
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
let column = value.as_bytes()?;
|
||||
match bincode::deserialize(column) {
|
||||
Ok(listing_state) => Ok(listing_state),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlx::Type<Postgres> for ListingType {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::with_name("bytea")
|
||||
}
|
||||
|
||||
fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
|
||||
*ty == Self::type_info()
|
||||
}
|
||||
}
|
10
src/dto/agent/mod.rs
Normal file
10
src/dto/agent/mod.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::domain::{agent::Agent, contact_info::ContactInformation};
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct AgentContainer {
|
||||
pub agent: Agent,
|
||||
pub contact: ContactInformation,
|
||||
}
|
2
src/dto/mod.rs
Normal file
2
src/dto/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod property;
|
||||
pub mod agent;
|
18
src/dto/property/mod.rs
Normal file
18
src/dto/property/mod.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::domain::{property::Property, property_details::PropertyDetails, location::Location};
|
||||
|
||||
use super::agent::AgentContainer;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub struct PropertyContainer {
|
||||
pub property: Property,
|
||||
pub details: PropertyDetails,
|
||||
pub location: Location,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub struct ListingContainer {
|
||||
property: PropertyContainer,
|
||||
agent: AgentContainer,
|
||||
}
|
2
src/lib.rs
Normal file
2
src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
Loading…
Reference in New Issue
Block a user