daummap/
address.rs

1use {
2    crate::{request, Meta, KAKAO_LOCAL_API_BASE_URL},
3    serde::Deserialize,
4};
5
6#[derive(Debug, Clone)]
7pub struct Address {
8    pub address: Option<String>,
9    pub land_lot: Option<LandLotAddress>,
10    pub road: Option<RoadAddress>,
11}
12
13#[derive(Debug, Clone)]
14pub struct LandLotAddress {
15    pub address: String,
16    pub province: String,
17    pub city: String,
18    pub town: String,
19    pub neighborhood: Option<String>,
20    pub h_code: Option<usize>,
21    pub b_code: Option<usize>,
22    pub is_mountain: Option<bool>,
23    pub main_address_number: Option<usize>,
24    pub sub_address_number: Option<usize>,
25    pub zip_code: Option<usize>,
26    pub longitude: Option<f32>,
27    pub latitude: Option<f32>,
28}
29
30#[derive(Debug, Clone)]
31pub struct RoadAddress {
32    pub address: String,
33    pub province: String,
34    pub city: String,
35    pub town: String,
36    pub road_name: String,
37    pub is_underground: bool,
38    pub main_building_number: Option<usize>,
39    pub sub_building_number: Option<usize>,
40    pub building_name: String,
41    pub post_code: Option<usize>,
42    pub longitude: Option<f32>,
43    pub latitude: Option<f32>,
44}
45
46#[derive(Debug, Clone)]
47pub struct AddressResponse {
48    pub addresses: Vec<Address>,
49    pub total_count: usize,
50    pub pageable_count: usize,
51    pub is_end: bool,
52}
53
54#[derive(Debug, Clone)]
55pub struct AddressRequest {
56    base_url: String,
57    app_key: String,
58    query: String,
59    page: usize,
60    size: usize,
61}
62
63impl AddressRequest {
64    pub fn new(app_key: &str, query: &str) -> Self {
65        AddressRequest {
66            base_url: KAKAO_LOCAL_API_BASE_URL.to_string(),
67            app_key: app_key.to_string(),
68            query: query.to_string(),
69            page: 1,
70            size: 15,
71        }
72    }
73
74    pub fn base_url(&mut self, base_url: &str) -> &mut Self {
75        self.base_url = base_url.to_string();
76        self
77    }
78
79    pub fn page(&mut self, page: usize) -> &mut Self {
80        self.page = page;
81        self
82    }
83
84    pub fn size(&mut self, size: usize) -> &mut Self {
85        self.size = size;
86        self
87    }
88
89    pub async fn get(&self) -> Result<AddressResponse, failure::Error> {
90        static API_PATH: &'static str = "/search/address.json";
91
92        let resp = request::<RawResponse>(
93            &self.base_url,
94            API_PATH,
95            &[
96                ("query", self.query.clone()),
97                ("page", self.page.to_string()),
98                ("size", self.size.to_string()),
99            ],
100            &self.app_key,
101        )
102        .await?;
103        let addresses = resp
104            .documents
105            .into_iter()
106            .map(|document| Address {
107                address: document.address_name,
108                land_lot: document.address.map(Into::into),
109                road: document.road_address.map(Into::into),
110            })
111            .filter(|addr| addr.land_lot.is_some() || addr.road.is_some())
112            .collect();
113
114        Ok(AddressResponse {
115            addresses,
116            total_count: resp.meta.total_count,
117            pageable_count: resp.meta.pageable_count,
118            is_end: resp.meta.is_end,
119        })
120    }
121}
122
123#[derive(Debug, Deserialize)]
124struct RawResponse {
125    documents: Vec<Document>,
126    meta: Meta,
127}
128
129#[derive(Debug, Deserialize)]
130struct Document {
131    address_name: Option<String>,
132    address: Option<RawLandLotAddress>,
133    road_address: Option<RawRoadAddress>,
134}
135
136#[derive(Debug, Deserialize)]
137struct RawLandLotAddress {
138    address_name: String,
139    region_1depth_name: String,
140    region_2depth_name: String,
141    region_3depth_name: String,
142    region_3depth_h_name: String,
143    h_code: String,
144    b_code: String,
145    mountain_yn: String,
146    main_address_no: String,
147    sub_address_no: String,
148    zip_code: String,
149    x: String,
150    y: String,
151}
152
153#[derive(Debug, Deserialize)]
154struct RawRoadAddress {
155    address_name: String,
156    region_1depth_name: String,
157    region_2depth_name: String,
158    region_3depth_name: String,
159    road_name: String,
160    underground_yn: String,
161    main_building_no: String,
162    sub_building_no: String,
163    building_name: String,
164    zone_no: String,
165    x: String,
166    y: String,
167}
168
169impl From<RawLandLotAddress> for LandLotAddress {
170    fn from(raddr: RawLandLotAddress) -> Self {
171        LandLotAddress {
172            address: raddr.address_name,
173            province: raddr.region_1depth_name,
174            city: raddr.region_2depth_name,
175            town: raddr.region_3depth_name,
176            neighborhood: if raddr.region_3depth_h_name.is_empty() {
177                None
178            } else {
179                Some(raddr.region_3depth_h_name)
180            },
181            h_code: raddr.h_code.parse::<usize>().ok(),
182            b_code: raddr.b_code.parse::<usize>().ok(),
183            is_mountain: if raddr.mountain_yn.is_empty() {
184                None
185            } else {
186                Some(raddr.mountain_yn == "Y")
187            },
188            main_address_number: raddr.main_address_no.parse::<usize>().ok(),
189            sub_address_number: raddr.sub_address_no.parse::<usize>().ok(),
190            zip_code: raddr.zip_code.parse::<usize>().ok(),
191            longitude: raddr.x.parse::<f32>().ok(),
192            latitude: raddr.y.parse::<f32>().ok(),
193        }
194    }
195}
196
197impl From<RawRoadAddress> for RoadAddress {
198    fn from(raddr: RawRoadAddress) -> Self {
199        RoadAddress {
200            address: raddr.address_name,
201            province: raddr.region_1depth_name,
202            city: raddr.region_2depth_name,
203            town: raddr.region_3depth_name,
204            road_name: raddr.road_name,
205            is_underground: raddr.underground_yn == "Y",
206            main_building_number: raddr.main_building_no.parse::<usize>().ok(),
207            sub_building_number: raddr.sub_building_no.parse::<usize>().ok(),
208            building_name: raddr.building_name,
209            post_code: raddr.zone_no.parse::<usize>().ok(),
210            longitude: raddr.x.parse::<f32>().ok(),
211            latitude: raddr.y.parse::<f32>().ok(),
212        }
213    }
214}