Skip to content

Commit

Permalink
Merge pull request #496 from YuukiToriyama/release/v0.1.22
Browse files Browse the repository at this point in the history
release/v0.1.22をmainブランチにマージ
  • Loading branch information
YuukiToriyama authored Nov 2, 2024
2 parents ee97b64 + 5f88631 commit ca13587
Show file tree
Hide file tree
Showing 21 changed files with 1,080 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.1.21"
version = "0.1.22"
edition = "2021"
description = "A Rust Library to parse japanese addresses."
repository = "https://github.com/YuukiToriyama/japanese-address-parser"
Expand Down
1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ regex = { version = "1.10.6", default-features = false, features = ["std", "unic
serde.workspace = true
reqwest = { version = "0.12.5", default-features = false, features = ["json", "rustls-tls"] }
js-sys = "0.3.67"
thiserror = "1.0.63"
jisx0401 = "0.1.0-beta.3"

[dev-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions core/src/domain.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
#[cfg(feature = "experimental")]
pub mod chimei_ruiju;
pub mod common;
pub mod geolonia;
2 changes: 2 additions & 0 deletions core/src/domain/chimei_ruiju.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod entity;
pub mod error;
63 changes: 63 additions & 0 deletions core/src/domain/chimei_ruiju/entity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use crate::domain::common::latlng::LatLng;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
pub struct PrefectureMaster {
/// 都道府県名
pub(crate) name: String,
/// 市区町村名リスト
pub(crate) cities: Vec<String>,
/// 代表点の緯度経度
pub(crate) coordinate: Coordinate,
}

#[derive(Deserialize, Debug)]
pub struct CityMaster {
/// 市区町村名
pub(crate) name: String,
/// 町名リスト
pub(crate) towns: Vec<String>,
/// 緯度経度
pub(crate) coordinate: Coordinate,
}

#[derive(Deserialize, Debug)]
pub struct TownMaster {
/// 町名
pub(crate) name: String,
/// 街区リスト
blocks: Vec<Block>,
/// 緯度経度
pub(crate) coordinate: Coordinate,
}

#[allow(dead_code)]
#[derive(Deserialize, Debug)]
pub struct Block {
/// 小字・通称名
koaza: String,
/// 街区符号・地番
block_number: String,
/// 住居表示の有無
residential_address_indication: bool,
/// 緯度経度
coordinate: Coordinate,
}

#[allow(dead_code)]
#[derive(Deserialize, Debug)]
pub struct Coordinate {
/// 緯度
pub(crate) latitude: f64,
/// 経度
pub(crate) longitude: f64,
}

impl Coordinate {
pub(crate) fn to_lat_lng(&self) -> LatLng {
LatLng {
latitude: self.latitude,
longitude: self.longitude,
}
}
}
11 changes: 11 additions & 0 deletions core/src/domain/chimei_ruiju/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use thiserror::Error;

#[derive(Error, PartialEq, Debug)]
pub enum ApiError {
#[error("network error occurs: {url}")]
Network { url: String },
#[error("resource not found: {url}")]
NotFound { url: String },
#[error("deserialize error occurs: {url}")]
Deserialize { url: String },
}
1 change: 1 addition & 0 deletions core/src/experimental.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
//!
//! If you are eager to use this module, please enable `experimental` feature flag.
mod parse_with_chimeiruiju;
mod parse_with_geolonia;
pub mod parser;
207 changes: 207 additions & 0 deletions core/src/experimental/parse_with_chimeiruiju.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use crate::domain::common::latlng::LatLng;
use crate::domain::common::token::Token;
use crate::experimental::parser::Parser;
use crate::interactor::chimei_ruiju::{ChimeiRuijuInteractor, ChimeiRuijuInteractorImpl};
use crate::tokenizer::Tokenizer;
use std::option::Option;

impl Parser {
pub(crate) async fn parse_with_chimeiruiju(
&self,
address: &str,
) -> (Vec<Token>, Option<LatLng>) {
let interactor = ChimeiRuijuInteractorImpl::default();
let tokenizer = Tokenizer::new(address);
let mut lat_lng: Option<LatLng> = None;

// 都道府県名の検出
let (prefecture, tokenizer) = match tokenizer.read_prefecture() {
Ok(found) => found,
Err(not_found) => {
if self.options.verbose {
log::error!("都道府県名の検出に失敗しました")
}
return (not_found.tokens, lat_lng);
}
};

// 都道府県マスタの取得
let prefecture_master = match interactor.get_prefecture_master(&prefecture).await {
Ok(result) => {
lat_lng.replace(result.coordinate.to_lat_lng());
result
}
Err(error) => {
if self.options.verbose {
log::error!("{}", error)
}
return (tokenizer.finish().tokens, lat_lng);
}
};
// 市区町村名の検出
let (city_name, tokenizer) = match tokenizer.read_city(&prefecture_master.cities) {
Ok(found) => found,
Err(not_found) => {
if self.options.correct_incomplete_city_names {
match not_found.read_city_with_county_name_completion(&prefecture_master.cities)
{
Ok(result) => result,
Err(not_found) => {
if self.options.verbose {
log::error!("市区町村名の検出に失敗しました")
}
return (not_found.tokens, lat_lng);
}
}
} else {
if self.options.verbose {
log::error!("市区町村名の検出に失敗しました")
}
return (not_found.finish().tokens, lat_lng);
}
}
};

// 市区町村マスタの取得
let city_master = match interactor.get_city_master(&prefecture, &city_name).await {
Ok(result) => {
lat_lng.replace(result.coordinate.to_lat_lng());
result
}
Err(error) => {
if self.options.verbose {
log::error!("{}", error)
}
return (tokenizer.finish().tokens, lat_lng);
}
};
// 町名の検出
let (town_name, tokenizer) = match tokenizer.read_town(city_master.towns) {
Ok(found) => found,
Err(not_found) => {
if self.options.verbose {
log::error!("町名の検出に失敗しました")
}
return (not_found.tokens, lat_lng);
}
};

// 町村マスタの取得
if let Ok(town_master) = interactor
.get_town_master(&prefecture, &city_name, &town_name)
.await
{
lat_lng.replace(town_master.coordinate.to_lat_lng());
};

(tokenizer.finish().tokens, lat_lng)
}
}

#[cfg(test)]
mod tests {
use crate::domain::common::token::{City, Prefecture, Token, Town};
use crate::experimental::parser::{DataSource, Parser, ParserOptions};

#[tokio::test]
async fn 都道府県名が誤っている場合() {
let parser = Parser {
options: ParserOptions {
data_source: DataSource::ChimeiRuiju,
correct_incomplete_city_names: false,
verbose: false,
},
};
let (tokens, _) = parser
.parse_with_chimeiruiju("奈川県横浜市磯子区洋光台3-10-3")
.await;
assert_eq!(
tokens,
vec![Token::Rest("奈川県横浜市磯子区洋光台3-10-3".to_string())]
)
}

#[tokio::test]
async fn 市区町村名が誤っている場合() {
let parser = Parser {
options: ParserOptions {
data_source: DataSource::ChimeiRuiju,
correct_incomplete_city_names: false,
verbose: false,
},
};
let (tokens, _) = parser
.parse_with_chimeiruiju("神奈川県横浜県磯子市洋光台3-10-3")
.await;
assert_eq!(
tokens,
vec![
Token::Prefecture(Prefecture {
prefecture_name: "神奈川県".to_string(),
representative_point: None,
}),
Token::Rest("横浜県磯子市洋光台3-10-3".to_string())
]
)
}

#[tokio::test]
async fn 町名が誤っている場合() {
let parser = Parser {
options: ParserOptions {
data_source: DataSource::ChimeiRuiju,
correct_incomplete_city_names: false,
verbose: false,
},
};
let (tokens, _) = parser
.parse_with_chimeiruiju("神奈川県横浜市磯子区陽光台3-10-3")
.await;
assert_eq!(
tokens,
vec![
Token::Prefecture(Prefecture {
prefecture_name: "神奈川県".to_string(),
representative_point: None,
}),
Token::City(City {
city_name: "横浜市磯子区".to_string(),
representative_point: None,
}),
Token::Rest("陽光台3-10-3".to_string())
]
)
}

#[tokio::test]
async fn パースに成功した場合() {
let parser = Parser {
options: ParserOptions {
data_source: DataSource::ChimeiRuiju,
correct_incomplete_city_names: false,
verbose: false,
},
};
let (tokens, _) = parser
.parse_with_chimeiruiju("神奈川県横浜市磯子区洋光台3-10-3")
.await;
assert_eq!(
tokens,
vec![
Token::Prefecture(Prefecture {
prefecture_name: "神奈川県".to_string(),
representative_point: None,
}),
Token::City(City {
city_name: "横浜市磯子区".to_string(),
representative_point: None,
}),
Token::Town(Town {
town_name: "洋光台三丁目".to_string(),
representative_point: None,
}),
Token::Rest("10-3".to_string())
]
)
}
}
34 changes: 23 additions & 11 deletions core/src/experimental/parser.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
use crate::domain::common::latlng::LatLng;
use crate::domain::common::token::Token;
use serde::Serialize;

/// Data source for Parser
///
/// パーサーで使用するデータソースを指定します。
#[derive(Debug)]
#[derive(Debug, Default)]
pub enum DataSource {
/// ChimeiRuiju 住所データ
/// <https://github.com/Cocon/chimei-ruiju>
ChimeiRuiju,
/// Geolonia 住所データ
/// <https://github.com/geolonia/japanese-addresses>
#[default]
Geolonia,
}

impl Default for DataSource {
fn default() -> Self {
DataSource::Geolonia
}
}

/// Options for Parser
///
/// パーサーのオプションを指定します。
Expand Down Expand Up @@ -89,10 +88,12 @@ impl Parser {
/// }
/// ```
pub async fn parse(&self, address: &str) -> ParsedAddress {
let tokens = match self.options.data_source {
DataSource::Geolonia => self.parse_with_geolonia(address).await,
};
ParsedAddress::from(tokens)
match self.options.data_source {
DataSource::ChimeiRuiju => {
ParsedAddress::from(self.parse_with_chimeiruiju(address).await)
}
DataSource::Geolonia => ParsedAddress::from(self.parse_with_geolonia(address).await),
}
}
}

Expand Down Expand Up @@ -185,6 +186,17 @@ impl From<Vec<Token>> for ParsedAddress {
}
}

impl From<(Vec<Token>, Option<LatLng>)> for ParsedAddress {
fn from((tokens, lat_lng): (Vec<Token>, Option<LatLng>)) -> Self {
let mut parsed_address = ParsedAddress::from(tokens);
if let Some(lat_lng) = lat_lng {
parsed_address.metadata.longitude = Some(lat_lng.longitude);
parsed_address.metadata.latitude = Some(lat_lng.latitude);
}
parsed_address
}
}

#[cfg(test)]
mod tests {
use crate::domain::common::latlng::LatLng;
Expand Down
2 changes: 2 additions & 0 deletions core/src/interactor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(feature = "experimental")]
pub mod chimei_ruiju;
Loading

0 comments on commit ca13587

Please sign in to comment.