2022/03/12

[Rust]zipcloudのAPIを使って郵便番号から住所を検索する

rust

概要

郵便番号から住所を検索することがありますが、そのzipcloudはPublicなAPIを提供していて、それを利用してRustから検索できるようにしてみました。

公式ページはこちら

※ 事前に利用規約を呼んで使ってください

Rustでの実装

必要なライブラリ

# レスポンス結果のjsonをパースするために使います
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

# httpリクエストを実行するために使います
http = "0.2"
hyper = { version = "0.14", features = ["client", "http2"] }
hyper-rustls = "0.23.0"

結果を格納する構造体です

#[derive(Clone, Debug, Deserialize)]
pub struct Response {
    pub message: Option<String>,
    pub results: Option<Vec<Address>>,
    pub status: Option<i32>,
}

#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct Address {
    pub address1: String,
    pub address2: String,
    pub address3: String,
    pub kana1: String,
    pub kana2: String,
    pub kana3: String,
    pub prefcode: String,
    pub zipcode: String,
}

メイン部分です

use http::{Request, StatusCode, Version};
use hyper::body::Buf;
use hyper_rustls::HttpsConnectorBuilder;
use serde::Deserialize;
use std::io::Read;

pub async fn fetch_address(zipcode: &str) -> Result<Option<Address>> {
    // hyperとhttpを使って、zipcloudにgetリクエストを投げます。
    let uri = format!("https://zipcloud.ibsnet.co.jp/api/search?zipcode={}&limit=1", zipcode);
    let request = Request::builder()
        .version(Version::HTTP_11)
        .method("GET")
        .uri(uri)
        .body(hyper::Body::empty())
        .unwrap();
    let connector = HttpsConnectorBuilder::new()
        .with_native_roots()
        .https_only()
        .enable_http1()
        .build();
    let client = hyper::Client::builder().http2_only(false).build(connector);
    let response = client.request(request).await?;

    // レスポンスを結果の構造体にパースして返却します。
    let response_status = response.status().clone();
    let mut response_body = String::new();
    hyper::body::aggregate(response.into_body())
        .await?
        .reader()
        .read_to_string(&mut response_body)?;
    if response_status == StatusCode::OK {
        let response: Response = serde_json::from_str(&response_body)?;
        if let Some(result) = response.results {
            let address = result.get(0).unwrap();
            Ok(Some(address.clone()))
        } else {
            Ok(None)
        }
    } else {
        Err(Error::Gateway {
            status_code: response_status.as_u16(),
            reason: response_body,
        })
    }
}

作ったライブラリ

createは公開していませんが・・・githubにソースコードを公開しました。

以下のような感じで使います。(郵便番号100-0000をもとに、千代田区の住所が取得できています。)

use zipcloud::Address;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let zipcode = "100-0000";
    let address = zipcloud::fetch_address(zipcode).await.unwrap();
    assert!(address.is_some());
    assert_eq!(
        Address {
            address1: "東京都".to_owned(),
            address2: "千代田区".to_owned(),
            address3: "".to_owned(),
            kana1: "トウキョウト".to_owned(),
            kana2: "チヨタ\u{ff9e}ク".to_owned(),
            kana3: "".to_owned(),
            prefcode: "13".to_owned(),
            zipcode: "1000000".to_owned()
        },
        address.unwrap()
    );
    Ok(())
}

以上です。