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}