use serde::{Deserialize, Deserializer, Serialize};
fn u32_string_or_int<'de, D: Deserializer<'de>>(d: D) -> Result<u32, D::Error> {
let v = serde_json::Value::deserialize(d)?;
match v {
serde_json::Value::String(s) => s.parse().map_err(serde::de::Error::custom),
serde_json::Value::Number(n) => n
.as_u64()
.and_then(|n| u32::try_from(n).ok())
.ok_or_else(|| serde::de::Error::custom(format!("u32 out of range: {n}"))),
other => Err(serde::de::Error::custom(format!(
"expected string or integer for u32, got {other:?}"
))),
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Community {
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommunityDetail {
pub id: u64,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub region: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub section: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub age: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub floor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub house_holds: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lat: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lng: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub build_purpose: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_area: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub const_company: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_count: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NearbyCommunity {
pub id: u64,
pub name: String,
pub region_id: u32,
pub section_id: u32,
pub region: String,
pub section: String,
pub price_unit: PriceValue,
pub address: String,
pub full_address: String,
pub age: String,
pub house_holds: String,
pub build_purpose: String,
pub distance: String,
pub sale_num: u32,
pub min_price: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceRecord {
pub id: u64,
pub date: String,
pub address: String,
pub layout: String,
pub build_area: String,
pub total_price: String,
pub unit_price: PriceValue,
pub shift_floor: String,
pub total_floor: String,
pub build_purpose_str: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceValue {
pub price: String,
pub unit: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SaleListing {
pub houseid: u64,
pub title: String,
pub price_v: PriceValue,
pub price_unit: String,
pub room: String,
pub address: String,
pub area_v: AreaValue,
pub floor: String,
pub floor_en: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub photo_src: Option<String>,
pub label: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AreaValue {
pub area: String,
pub unit: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchListing {
pub post_id: u64,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub price: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_unit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub area: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub room: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub floor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub photo_list: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_time: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct SearchParams {
pub region_id: u32,
pub kind: Option<u32>,
pub price_max: Option<u32>,
pub price_min: Option<u32>,
pub order: Option<String>,
pub limit: usize,
pub first_row: usize,
}
#[derive(Debug, Clone)]
pub struct CrawlOptions {
pub max_pages: usize,
pub delay_ms: u64,
pub retries: u32,
pub start_page: usize,
}
impl Default for CrawlOptions {
fn default() -> Self {
Self {
max_pages: 0,
delay_ms: 1000,
retries: 3,
start_page: 0,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct Region {
pub id: u32,
pub name: &'static str,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SaleHouseListing {
pub houseid: u64,
pub title: String,
pub address: String,
pub area: f64,
pub price: u64,
pub showprice: String,
pub unit_price: String,
pub floor: String,
pub room: String,
pub kind_name: String,
pub shape_name: String,
pub region_id: u32,
pub region_name: String,
pub section_id: u32,
pub section_name: String,
pub houseage: u32,
pub showhouseage: String,
pub posttime: i64,
pub browsenum: u64,
pub photo_url: String,
#[serde(rename = "photoNum", deserialize_with = "u32_string_or_int")]
pub photo_num: u32,
pub nick_name: String,
#[serde(default)]
pub tag: Vec<String>,
pub community_name: String,
#[serde(default)]
pub community_link: String,
#[serde(default)]
pub community_sale_num: u32,
pub refreshtime: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SaleHousePage {
pub total: u32,
pub first_row: usize,
pub houses: Vec<SaleHouseListing>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseProject {
pub hid: u64,
pub community_id: u64,
pub build_name: String,
pub address: String,
pub area: String,
pub room: String,
pub price: String,
pub price_unit: String,
pub purpose_str: String,
pub region: String,
#[serde(rename = "regionid")]
pub region_id: u32,
pub section: String,
#[serde(rename = "sectionid")]
pub section_id: u32,
pub shop_name: String,
pub cover: String,
pub phone: String,
pub phone_ext: String,
#[serde(default)]
pub tag: Vec<String>,
pub updatetime: String,
pub is_video: u8,
pub group_type_txt: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhousePage {
pub total: u32,
pub online_total: u32,
pub page: u32,
pub per_page: u32,
pub total_page: u32,
pub items: Vec<NewhouseProject>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhotoSrc {
pub src: String,
#[serde(default, rename = "type")]
pub kind: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommunityRankItem {
pub cid: u64,
pub name: String,
pub address: String,
pub photo_src: PhotoSrc,
pub price: PriceValue,
pub price_num: u64,
pub sale_num: u32,
pub shop_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommunityRanks {
pub price_data: Vec<CommunityRankItem>,
pub sale_data: Vec<CommunityRankItem>,
pub time: String,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct RentListResponse {
pub status: i32,
pub data: Option<RentListData>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct RentListData {
#[serde(default, deserialize_with = "u32_string_or_int_default")]
pub total: u32,
pub items: Vec<RentListItem>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct RentListItem {
pub id: u64,
pub title: String,
#[serde(default)]
pub price: Option<String>,
#[serde(default)]
pub price_unit: Option<String>,
#[serde(default)]
pub address: Option<String>,
#[serde(default)]
pub area_name: Option<String>,
#[serde(default)]
pub kind_name: Option<String>,
#[serde(default, rename = "layoutStr")]
pub layout_str: Option<String>,
#[serde(default)]
pub floor_name: Option<String>,
#[serde(default, rename = "photoList")]
pub photo_list: Option<Vec<String>>,
#[serde(default)]
pub tags: Option<Vec<String>>,
#[serde(default)]
pub refresh_time: Option<String>,
}
impl From<RentListItem> for SearchListing {
fn from(v: RentListItem) -> Self {
let room = v.layout_str.filter(|s| !s.is_empty());
let photo_list = v.photo_list.filter(|v| !v.is_empty());
let tags = v.tags.filter(|v| !v.is_empty());
SearchListing {
post_id: v.id,
title: v.title,
price: v.price,
price_unit: v.price_unit,
address: v.address,
area: v.area_name,
kind_name: v.kind_name,
room,
floor: v.floor_name,
photo_list,
tags,
post_time: v.refresh_time,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PendingPrice {
pub pending: u8,
pub price: String,
pub unit: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PendingArea {
pub pending: u8,
pub area: String,
#[serde(default)]
pub area_min: String,
pub unit: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PendingRoom {
pub pending: u8,
pub layout: String,
pub unit: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DealTime {
#[serde(rename = "type")]
pub kind: String,
pub date: String,
pub deal: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseHousing {
pub hid: u64,
pub build_name: String,
pub address: String,
#[serde(default)]
pub region: Option<String>,
pub regionid: u32,
#[serde(default)]
pub section: Option<String>,
pub sectionid: u32,
#[serde(default, deserialize_with = "u64_string_or_int_default")]
pub community_id: u64,
pub build_type_name: String,
pub purpose_name: String,
pub price: PendingPrice,
pub area: PendingArea,
pub layout: PendingRoom,
pub households: String,
pub manage_cost: PendingPrice,
pub cover: String,
pub shop_name: String,
#[serde(default)]
pub tag: Vec<String>,
#[serde(default, deserialize_with = "u32_string_or_int_default")]
pub open_sell_time: u32,
pub deal_time: DealTime,
pub build_company: String,
pub sell_company: String,
pub structural_engine: String,
pub floor: String,
pub decorate: String,
pub park_ratio: String,
pub license: String,
pub use_license: String,
pub browsenum: u64,
pub fav_num: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseMarketRoom {
pub name: String,
pub price: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseMarketItem {
pub id: u64,
pub trans_date: String,
pub month: String,
pub layout_v2: String,
pub build_area_v: AreaValue,
pub building_area: AreaValue,
pub unit_price: PriceValue,
pub total_price_v: String,
pub shift_floor: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseMarket {
pub housing_id: u64,
pub housing_name: String,
#[serde(default, deserialize_with = "u64_string_or_int_default")]
pub community_id: u64,
#[serde(default)]
pub rooms: Vec<NewhouseMarketRoom>,
#[serde(default)]
pub items: Vec<NewhouseMarketItem>,
#[serde(default, deserialize_with = "u64_string_or_int_default")]
pub total: u64,
#[serde(default)]
pub update_date: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseLayoutBlock {
#[serde(default, deserialize_with = "u64_string_or_int_default")]
pub total: u64,
#[serde(default)]
pub items: Vec<serde_json::Value>,
#[serde(default)]
pub room_group: Vec<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseSalesAgent {
pub user_id: u64,
pub realname: String,
pub mobile_v2: String,
#[serde(default)]
pub email: String,
#[serde(default)]
pub introduction: String,
pub avatar: String,
#[serde(default)]
pub tags: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseModules {
pub layout: NewhouseLayoutBlock,
pub market: Option<NewhouseMarket>,
pub sales_agents: Vec<NewhouseSalesAgent>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhouseModuleInfoResponse {
pub status: i32,
pub msg: Option<String>,
pub data: Option<NewhouseModuleInfoData>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhouseModuleInfoData {
#[serde(default = "default_layout")]
pub layout: NewhouseLayoutBlock,
pub market: Option<NewhouseMarket>,
#[serde(default)]
pub sales: NewhouseSalesData,
}
fn default_layout() -> NewhouseLayoutBlock {
NewhouseLayoutBlock {
total: 0,
items: vec![],
room_group: vec![],
}
}
#[derive(Debug, Clone, Default, Deserialize)]
pub(crate) struct NewhouseSalesData {
#[serde(default)]
pub data: Vec<NewhouseSalesAgent>,
}
#[derive(Debug, Clone, Serialize)]
pub struct NewhouseDetail {
pub housing: Option<NewhouseHousing>,
#[serde(skip_serializing_if = "Option::is_none")]
pub housing_error: Option<String>,
pub modules: Option<NewhouseModules>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modules_error: Option<String>,
pub photos: Vec<NewhousePhotoCategory>,
#[serde(skip_serializing_if = "Option::is_none")]
pub photos_error: Option<String>,
pub surrounding: Option<NewhouseSurrounding>,
#[serde(skip_serializing_if = "Option::is_none")]
pub surrounding_error: Option<String>,
pub nearby_market: Option<NewhouseNearbyMarket>,
#[serde(skip_serializing_if = "Option::is_none")]
pub nearby_market_error: Option<String>,
pub price_list: Option<NewhousePriceList>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_list_error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseSaleCtrlPrice {
pub id: u64,
pub address: String,
pub room: String,
pub unit_price: PriceValue,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseSaleCtrlInfo {
pub update_count: u64,
pub price: NewhouseSaleCtrlPrice,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhousePriceList {
pub housing_id: u64,
pub housing_name: String,
#[serde(default, deserialize_with = "u64_string_or_int_default")]
pub community_id: u64,
#[serde(default)]
pub rooms: Vec<NewhouseMarketRoom>,
#[serde(default)]
pub has_sale_ctrl: u8,
pub sale_ctrl_info: Option<NewhouseSaleCtrlInfo>,
#[serde(default)]
pub items: Vec<NewhouseMarketItem>,
#[serde(default, deserialize_with = "u64_string_or_int_default")]
pub total: u64,
#[serde(default)]
pub update_date: String,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhousePriceListResponse {
pub status: i32,
pub msg: Option<String>,
pub data: Option<NewhousePriceList>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentUnit {
pub content: String,
pub unit: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseNearbyComm {
pub community_id: u64,
pub community_name: String,
pub deal_count: u64,
pub price: ContentUnit,
pub community_image: String,
#[serde(default)]
pub build_type: u8,
pub build_type_str: String,
pub build_purpose: String,
pub layout: ContentUnit,
pub area: ContentUnit,
#[serde(default, deserialize_with = "u32_string_or_int_default")]
pub age: u32,
pub distance: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseNearbyBusiness {
pub id: u64,
pub shop_id: u64,
pub name: String,
pub price_unit: String,
pub unit: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseNearbyMarket {
#[serde(default)]
pub community_items: Vec<NewhouseNearbyComm>,
#[serde(default)]
pub business_items: Vec<NewhouseNearbyBusiness>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhouseNearbyMarketResponse {
pub status: i32,
pub msg: Option<String>,
pub data: Option<NewhouseNearbyMarket>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhousePoi {
pub name: String,
pub distance: u64,
pub distance_text: String,
pub lat: f64,
pub lng: f64,
pub sub_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseSurroundingFacility {
#[serde(default, deserialize_with = "u64_string_or_int_default")]
pub total: u64,
#[serde(default)]
pub traffic: Vec<NewhousePoi>,
#[serde(default)]
pub education: Vec<NewhousePoi>,
#[serde(default)]
pub life: Vec<NewhousePoi>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MapCoord {
pub lat: String,
pub lng: String,
#[serde(default)]
pub pending: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseSurroundingHousing {
pub hid: u64,
pub build_name: String,
pub address: String,
pub map: MapCoord,
#[serde(default)]
pub reception_address: String,
pub reception_map: MapCoord,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhouseSurrounding {
pub facility: NewhouseSurroundingFacility,
pub housing: NewhouseSurroundingHousing,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhouseSurroundingResponse {
pub status: i32,
pub msg: Option<String>,
pub data: Option<NewhouseSurrounding>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhousePhoto {
pub id: u64,
pub cate: String,
pub cate_name: String,
#[serde(default)]
pub note: String,
pub src_img: String,
#[serde(default)]
pub small_img: String,
#[serde(default)]
pub big_img: String,
#[serde(default)]
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewhousePhotoCategory {
pub id: String,
pub name: String,
pub build_name: String,
pub total: u64,
#[serde(default)]
pub items: Vec<NewhousePhoto>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhousePhotosResponse {
pub status: i32,
pub msg: Option<String>,
pub data: Option<Vec<NewhousePhotoCategory>>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhouseBaseInfoResponse {
pub status: i32,
pub msg: Option<String>,
pub data: Option<NewhouseBaseInfoData>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct NewhouseBaseInfoData {
pub housing: Option<NewhouseHousing>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceRange {
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AreaRange {
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<f64>,
}
#[derive(Debug, Clone, Serialize)]
pub struct HighValueParams {
pub region_id: u32,
pub kind: u32,
#[serde(rename = "type")]
pub kind_type: u32,
#[serde(default)]
pub section_id: Vec<u32>,
#[serde(default)]
pub shape: Vec<String>,
#[serde(default)]
pub room: Vec<u32>,
#[serde(default)]
pub price: Vec<PriceRange>,
#[serde(default)]
pub area: Vec<AreaRange>,
}
impl HighValueParams {
pub fn for_region(region_id: u32) -> Self {
Self {
region_id,
kind: 9,
kind_type: 2,
section_id: vec![],
shape: vec![],
room: vec![],
price: vec![],
area: vec![],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HighValueListing {
pub post_id: u64,
pub title: String,
#[serde(rename = "type")]
pub kind_type: u32,
pub kind: u32,
#[serde(deserialize_with = "u64_string_or_int")]
pub price: u64,
pub unit: String,
pub area: f64,
pub area_unit: String,
pub unit_price: f64,
pub room: u32,
pub hall: u32,
pub toilet: u32,
pub layout: String,
pub region_name: String,
pub section_name: String,
pub street_name: String,
pub cover: String,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct HighValueSearchResponse {
pub status: i32,
#[serde(default)]
pub msg: String,
#[serde(default)]
pub data: Vec<HighValueListing>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoordArea {
pub region_id: u32,
pub region_name: String,
pub section_id: u32,
pub section_name: String,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct CoordResponse {
pub status: i32,
#[serde(default)]
pub msg: String,
#[serde(default)]
pub data: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LabelledValue {
pub name: String,
pub value: String,
pub key: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentTag {
pub id: u32,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentRemark {
#[serde(default)]
pub title: String,
#[serde(default)]
pub key: String,
#[serde(default)]
pub active: u32,
#[serde(default)]
pub content: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentServiceItem {
pub key: String,
pub name: String,
#[serde(default)]
pub active: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentNoticeItem {
pub key: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentServiceTable {
#[serde(default)]
pub title: String,
#[serde(default)]
pub active: u32,
#[serde(default)]
pub facility: Vec<RentServiceItem>,
#[serde(default)]
pub notice: Vec<RentNoticeItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RentPublish {
pub id: u32,
pub name: String,
pub key: String,
#[serde(default)]
pub post_time: String,
#[serde(default)]
pub update_time: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentAddress {
pub data: String,
#[serde(default)]
pub value: String,
pub lat: String,
pub lng: String,
#[serde(default)]
pub traffic: String,
#[serde(default)]
pub station: String,
#[serde(default)]
pub distance: String,
}
pub type RentLinkInfo = serde_json::Value;
pub trait RentLinkInfoExt {
fn link_str(&self, key: &str) -> Option<&str>;
fn link_u64(&self, key: &str) -> Option<u64>;
fn link_u32(&self, key: &str) -> Option<u32>;
}
impl RentLinkInfoExt for RentLinkInfo {
fn link_str(&self, key: &str) -> Option<&str> {
if let Some(obj) = self.as_object() {
obj.get(key).and_then(|v| v.as_str())
} else if let Some(arr) = self.as_array() {
arr.iter()
.find(|e| e.get("key").and_then(|k| k.as_str()) == Some(key))
.and_then(|e| e.get("value").and_then(|v| v.as_str()))
} else {
None
}
}
fn link_u64(&self, key: &str) -> Option<u64> {
if let Some(obj) = self.as_object() {
obj.get(key).and_then(|v| v.as_u64())
} else if let Some(arr) = self.as_array() {
arr.iter()
.find(|e| e.get("key").and_then(|k| k.as_str()) == Some(key))
.and_then(|e| e.get("value").and_then(|v| v.as_u64()))
} else {
None
}
}
fn link_u32(&self, key: &str) -> Option<u32> {
self.link_u64(key).and_then(|v| u32::try_from(v).ok())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentFactTable {
pub title: Option<String>,
#[serde(default)]
pub active: u32,
#[serde(default)]
pub data: Vec<LabelledValue>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RentSurroundPoi {
#[serde(rename = "type")]
pub kind: String,
pub name: String,
pub distance: u64,
#[serde(default)]
pub distance_txt: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentSurroundCategory {
pub name: String,
pub key: String,
#[serde(default)]
pub children: Vec<RentSurroundPoi>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentSurround {
pub title: String,
pub key: String,
pub address: String,
pub lat: String,
pub lng: String,
#[serde(default)]
pub data: Vec<RentSurroundCategory>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RentDetail {
pub title: String,
pub price: String,
pub price_unit: String,
pub deposit: String,
pub head_info: String,
pub address: RentAddress,
pub region_id: u32,
pub section_id: u32,
pub kind: u32,
pub status: String,
#[serde(default)]
pub info: Vec<LabelledValue>,
pub cost: RentFactTable,
pub house_info: RentFactTable,
pub preference: RentFactTable,
pub service: RentServiceTable,
pub surround: RentSurround,
pub tags: Vec<RentTag>,
pub publish: RentPublish,
pub remark: RentRemark,
pub link_info: RentLinkInfo,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct RentDetailResponse {
pub status: i32,
#[serde(default)]
pub msg: String,
pub data: Option<RentDetail>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RentPhoto {
pub photo_id: u64,
pub photo: String,
#[serde(default)]
pub orig_photo: String,
#[serde(default)]
pub thumb_photo: String,
#[serde(default)]
pub is_cover: u32,
#[serde(default)]
pub purpose: u32,
#[serde(default)]
pub note: String,
#[serde(default, rename = "type")]
pub kind: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RentPhotoGroup {
pub key: String,
#[serde(default)]
pub items: Vec<RentPhoto>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct RentPhotosResponse {
pub status: i32,
#[serde(default)]
pub msg: String,
pub data: Option<RentPhotosData>,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct RentPhotosData {
#[serde(default)]
pub list: Vec<RentPhotoGroup>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SaleDetail {
pub id: String,
pub title: String,
pub price: String,
pub price_value: String,
pub unitprice: String,
pub area: String,
pub area_value: String,
pub layout: String,
pub room: String,
pub hall: String,
pub toilet: String,
pub kind: String,
pub kind_id: String,
pub region: String,
pub region_id: String,
pub section: String,
pub section_id: String,
#[serde(default)]
pub addr: String,
pub lat: String,
pub lng: String,
#[serde(default)]
pub age: String,
#[serde(default)]
pub houseage: String,
#[serde(default)]
pub shape: String,
#[serde(default)]
pub fitment: String,
#[serde(default)]
pub direction: String,
#[serde(default)]
pub lift: String,
#[serde(default)]
pub parking: String,
#[serde(default)]
pub mainarea: String,
#[serde(default)]
pub managefee: String,
pub posttime: String,
#[serde(default)]
pub community: String,
#[serde(default)]
pub community_id: String,
pub linkman: String,
#[serde(default)]
pub mobile: String,
#[serde(default)]
pub telephone: String,
#[serde(default)]
pub email: String,
pub identity: String,
#[serde(default)]
pub company_name: String,
#[serde(default)]
pub certificate_type: String,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct SaleDetailResponse {
pub status: i32,
#[serde(default)]
pub msg: Option<String>,
pub data: Option<SaleDetail>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SaleSimilarWare {
#[serde(rename = "type")]
pub kind_type: String,
pub post_id: String,
pub title: String,
pub price: String,
pub area: String,
pub kind: String,
#[serde(default)]
pub room: String,
pub section_name: String,
pub photo_url: String,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub similar_type: String,
#[serde(default)]
pub is_vip: String,
#[serde(default)]
pub is_refresh: String,
#[serde(default)]
pub is_combine: String,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct SaleSimilarWaresResponse {
pub status: i32,
#[serde(default)]
pub msg: Option<String>,
#[serde(default)]
pub data: Vec<SaleSimilarWare>,
}
#[derive(Deserialize)]
pub(crate) struct HotResponse {
pub status: i32,
pub data: Option<Vec<Community>>,
}
#[derive(Deserialize)]
pub(crate) struct NearbyResponse {
pub status: i32,
pub data: Option<Vec<NearbyApiItem>>,
}
#[derive(Deserialize)]
pub(crate) struct NearbyApiItem {
pub id: u64,
pub name: String,
pub regionid: u32,
pub sectionid: u32,
pub region: String,
pub section: String,
pub price_unit: PriceValue,
pub address: String,
pub full_address: String,
pub age: String,
pub house_holds: String,
pub build_purpose: String,
pub distance: String,
pub sale_num: u32,
#[serde(deserialize_with = "u64_string_or_int")]
pub min_price: u64,
}
impl From<NearbyApiItem> for NearbyCommunity {
fn from(v: NearbyApiItem) -> Self {
NearbyCommunity {
id: v.id,
name: v.name,
region_id: v.regionid,
section_id: v.sectionid,
region: v.region,
section: v.section,
price_unit: v.price_unit,
address: v.address,
full_address: v.full_address,
age: v.age,
house_holds: v.house_holds,
build_purpose: v.build_purpose,
distance: v.distance,
sale_num: v.sale_num,
min_price: v.min_price,
}
}
}
#[derive(Deserialize)]
pub(crate) struct DetailResponse {
pub status: i32,
pub data: Option<DetailData>,
}
#[derive(Deserialize)]
pub(crate) struct SaleListResponse {
pub status: i32,
pub data: Option<SaleListData>,
}
#[derive(Deserialize, Default)]
pub(crate) struct SaleListData {
#[serde(default, deserialize_with = "u32_string_or_int_default")]
pub total: u32,
#[serde(default)]
pub house_list: Vec<serde_json::Value>,
}
fn u64_string_or_int<'de, D: Deserializer<'de>>(d: D) -> Result<u64, D::Error> {
let v = serde_json::Value::deserialize(d)?;
match v {
serde_json::Value::String(s) => s.parse().map_err(serde::de::Error::custom),
serde_json::Value::Number(n) => n
.as_u64()
.ok_or_else(|| serde::de::Error::custom(format!("u64 out of range: {n}"))),
other => Err(serde::de::Error::custom(format!(
"expected string or integer for u64, got {other:?}"
))),
}
}
fn u64_string_or_int_default<'de, D: Deserializer<'de>>(d: D) -> Result<u64, D::Error> {
let v = serde_json::Value::deserialize(d)?;
match v {
serde_json::Value::Null => Ok(0),
serde_json::Value::String(s) => s.parse().map_err(serde::de::Error::custom),
serde_json::Value::Number(n) => n
.as_u64()
.ok_or_else(|| serde::de::Error::custom(format!("u64 out of range: {n}"))),
other => Err(serde::de::Error::custom(format!(
"expected null/string/integer, got {other:?}"
))),
}
}
fn u32_string_or_int_default<'de, D: Deserializer<'de>>(d: D) -> Result<u32, D::Error> {
let v = serde_json::Value::deserialize(d)?;
match v {
serde_json::Value::Null => Ok(0),
serde_json::Value::String(s) => s.parse().map_err(serde::de::Error::custom),
serde_json::Value::Number(n) => n
.as_u64()
.and_then(|n| u32::try_from(n).ok())
.ok_or_else(|| serde::de::Error::custom(format!("u32 out of range: {n}"))),
other => Err(serde::de::Error::custom(format!(
"expected null/string/integer, got {other:?}"
))),
}
}
#[derive(Deserialize)]
pub(crate) struct NewhouseListResponse {
pub status: i32,
pub data: Option<NewhouseListData>,
}
#[derive(Deserialize)]
pub(crate) struct NewhouseListData {
pub total: u32,
pub online_total: u32,
pub page: u32,
pub per_page: u32,
pub total_page: u32,
pub items: Vec<serde_json::Value>,
}
#[derive(Deserialize)]
pub(crate) struct CommunityRankResponse {
pub status: i32,
pub data: Option<CommunityRankApiData>,
}
#[derive(Deserialize)]
pub(crate) struct CommunityRankApiData {
pub price_data: CommunityRankSlot,
pub sale_data: CommunityRankSlot,
}
#[derive(Deserialize)]
pub(crate) struct CommunityRankSlot {
pub data: Vec<CommunityRankItem>,
pub time: String,
}
#[derive(Deserialize)]
pub(crate) struct DetailData {
pub community: Option<CommunityDetail>,
pub price: Option<PriceData>,
pub sale: Option<SaleData>,
}
#[derive(Deserialize)]
pub(crate) struct PriceData {
pub items: Vec<PriceRecord>,
}
#[derive(Deserialize)]
pub(crate) struct SaleData {
pub total: u32,
pub rooms: Vec<SaleRoom>,
}
#[derive(Deserialize)]
pub(crate) struct SaleRoom {
pub items: Vec<SaleListing>,
}