First functional commit

This commit is contained in:
Franklin 2023-03-09 13:44:38 -04:00
commit 54a576e853
16 changed files with 1869 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1504
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

15
Cargo.toml Normal file
View 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
View 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>,
}

View 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,
}

View File

@ -0,0 +1 @@
pub mod property;

View 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),
}

View 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
View 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
View 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()
}
}

View 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()
}
}

View 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
View 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
View File

@ -0,0 +1,2 @@
pub mod property;
pub mod agent;

18
src/dto/property/mod.rs Normal file
View 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
View File

@ -0,0 +1,2 @@
pub mod domain;
pub mod dto;