Skip to content

Commit

Permalink
Merge pull request #25 from YuukiToriyama/feature/adapt-variety-of-sp…
Browse files Browse the repository at this point in the history
…elling/master

地名の表記ゆれへの対応をmainにマージ
  • Loading branch information
YuukiToriyama authored Dec 3, 2023
2 parents 63c5799 + a7fa8c3 commit 3e94bb1
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 29 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ crate-type = ["rlib", "cdylib"]

[dependencies]
console_error_panic_hook = "0.1.7"
itertools = "0.12.0"
nom = "7.1.3"
reqwest = { version = "0.11.22", default-features = false, features = ["json", "rustls-tls", "blocking"] }
serde = { version = "1.0.192", features = ["derive"] }
Expand Down
21 changes: 11 additions & 10 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::parser::read_city::read_city;
use crate::parser::read_prefecture::read_prefecture;
use crate::parser::read_town::read_town;

mod adapter;
mod read_city;
mod read_prefecture;
mod read_town;
Expand Down Expand Up @@ -41,28 +42,28 @@ pub async fn parse<T: Api>(api: T, input: &str) -> ParseResult {
Some(result) => result,
};
// その市町村の町名リストを取得
let city = match api.get_city_master(prefecture_name, city_name).await {
let city = match api.get_city_master(prefecture_name, &city_name).await {
Err(error) => {
return ParseResult {
address: Address::new(prefecture_name, city_name, "", rest),
address: Address::new(prefecture_name, &city_name, "", &rest),
error: Some(error),
}
}
Ok(result) => result,
};
// 町名を特定
let (rest, town_name) = match read_town(rest, city) {
let (rest, town_name) = match read_town(&rest, city) {
None => {
return ParseResult {
address: Address::new(prefecture_name, city_name, "", rest),
address: Address::new(prefecture_name, &city_name, "", &rest),
error: Some(Error::new_parse_error(ParseErrorKind::TOWN)),
}
}
Some(result) => result,
};

ParseResult {
address: Address::new(prefecture_name, city_name, town_name, rest),
address: Address::new(prefecture_name, &city_name, &town_name, &rest),
error: None,
}
}
Expand Down Expand Up @@ -198,27 +199,27 @@ pub fn parse_blocking<T: BlockingApi>(api: T, input: &str) -> ParseResult {
}
Some(result) => result,
};
let city = match api.get_city_master(prefecture_name, city_name) {
let city = match api.get_city_master(prefecture_name, &city_name) {
Err(error) => {
return ParseResult {
address: Address::new(prefecture_name, city_name, "", rest),
address: Address::new(prefecture_name, &city_name, "", &rest),
error: Some(error),
};
}
Ok(result) => result,
};
let (rest, town_name) = match read_town(rest, city) {
let (rest, town_name) = match read_town(&rest, city) {
None => {
return ParseResult {
address: Address::new(prefecture_name, city_name, "", rest),
address: Address::new(prefecture_name, &city_name, "", &rest),
error: Some(Error::new_parse_error(ParseErrorKind::TOWN)),
};
}
Some(result) => result,
};

ParseResult {
address: Address::new(prefecture_name, city_name, town_name, rest),
address: Address::new(prefecture_name, &city_name, &town_name, &rest),
error: None,
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/parser/adapter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use itertools::Itertools;
use nom::bytes::complete::tag;
use nom::error::VerboseError;
use nom::Parser;

pub fn adapt_variety_of_spelling(
input: &str,
region_name: &String,
variety_of_spelling: Vec<&str>,
) -> Option<(String, String)> {
if variety_of_spelling.iter().all(|s| region_name.contains(s) == false) {
return None;
}
for permutation in variety_of_spelling.iter().permutations(2) {
if region_name.contains(permutation[0]) {
let edited_region_name = region_name.replace(permutation[0], permutation[1]);
if let Ok((rest, _)) = tag::<&str, &str, VerboseError<&str>>(&edited_region_name).parse(input) {
return Some((rest.to_string(), region_name.to_string()));
};
}
}
None
}
55 changes: 52 additions & 3 deletions src/parser/read_city.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
use crate::entity::Prefecture;
use crate::parser::adapter::adapt_variety_of_spelling;
use nom::bytes::complete::tag;
use nom::error::VerboseError;
use nom::Parser;

pub fn read_city(input: &str, prefecture: Prefecture) -> Option<(&str, &str)> {
pub fn read_city(input: &str, prefecture: Prefecture) -> Option<(String, String)> {
for city_name in prefecture.cities {
match tag::<&str, &str, VerboseError<&str>>(city_name.as_str()).parse(input) {
Ok(result) => return Some(result),
match tag::<&str, &str, VerboseError<&str>>(&city_name).parse(input) {
Ok((rest, city_name)) => return Some((rest.to_string(), city_name.to_string())),
Err(_) => {}
};
// 「ケ」「ヶ」「が」の表記ゆれに対応する
if let Some(result) = adapt_variety_of_spelling(input, &city_name, vec!["ケ", "ヶ", "が"]) {
return Some(result);
}
}
None
Expand Down Expand Up @@ -37,4 +42,48 @@ mod parser_tests {
);
assert_eq!(read_city("港区芝公園4丁目2-8", prefecture), None);
}

#[test]
fn read_city_表記ゆれ_茅ヶ崎市() {
let prefecture = Prefecture::new(
"神奈川県",
vec!["鎌倉市", "藤沢市", "小田原市", "茅ヶ崎市", "逗子市"],
);
let (rest, city) = read_city("茅ケ崎市香川5丁目1", prefecture).unwrap();
assert_eq!(rest, "香川5丁目1");
assert_eq!(city, "茅ヶ崎市");
}

#[test]
fn read_city_表記ゆれ_横浜市保土ケ谷区() {
let prefecture = Prefecture::new(
"神奈川県",
vec![
"横浜市中区",
"横浜市南区",
"横浜市保土ケ谷区",
"横浜市磯子区",
],
);
let (rest, city) = read_city("横浜市保土ヶ谷区川辺町2番地9", prefecture).unwrap();
assert_eq!(rest, "川辺町2番地9");
assert_eq!(city, "横浜市保土ケ谷区");
}

#[test]
fn read_city_表記ゆれ_不破郡関ケ原町() {
let prefecture = Prefecture::new(
"岐阜県",
vec![
"養老郡養老町",
"不破郡垂井町",
"不破郡関ケ原町",
"安八郡神戸町",
"安八郡輪之内町",
],
);
let (rest, city) = read_city("不破郡関が原町大字関ケ原894番地の58", prefecture).unwrap();
assert_eq!(rest, "大字関ケ原894番地の58");
assert_eq!(city, "不破郡関ケ原町");
}
}
83 changes: 67 additions & 16 deletions src/parser/read_town.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
use crate::entity::City;
use crate::parser::adapter::adapt_variety_of_spelling;
use nom::bytes::complete::tag;
use nom::error::VerboseError;
use nom::Parser;

pub fn read_town(input: &str, city: City) -> Option<(&str, &str)> {
pub fn read_town(input: &str, city: City) -> Option<(String, String)> {
for town in city.towns {
match tag::<&str, &str, VerboseError<&str>>(town.name.as_str()).parse(input) {
Ok(result) => return Some(result),
Err(_) => {}
if let Ok((rest, town_name)) = tag::<&str, &str, VerboseError<&str>>(town.name.as_str()).parse(input) {
return Some((rest.to_string(), town_name.to_string()));
}
// 「の」「ノ」の表記ゆれに対応する
if let Some(result) = adapt_variety_of_spelling(input, &town.name, vec!["の", "ノ"]) {
return Some(result);
}
// 「ツ」「ッ」の表記ゆれに対応する
if let Some(result) = adapt_variety_of_spelling(input, &town.name, vec!["ツ", "ッ"]) {
return Some(result);
}
// 「ケ」「ヶ」「が」の表記ゆれに対応する
if let Some(result) = adapt_variety_of_spelling(input, &town.name, vec!["ケ", "ヶ", "が"])
{
return Some(result);
}
}
None
Expand All @@ -23,18 +36,8 @@ mod parser_tests {
let city = City {
name: "静岡市清水区".to_string(),
towns: vec![
Town {
name: "旭町".to_string(),
koaza: "".to_string(),
lat: Some(35.016292),
lng: Some(138.489362),
},
Town {
name: "新丹谷".to_string(),
koaza: "".to_string(),
lat: Some(35.072403),
lng: Some(138.474199),
},
Town::new("旭町", "", 35.016292, 138.489362),
Town::new("新丹谷", "", 35.072403, 138.474199),
],
};
let (rest, town) = read_town("旭町6-8", city).unwrap();
Expand All @@ -50,4 +53,52 @@ mod parser_tests {
};
assert_eq!(read_town("旭町6-8", city), None);
}

#[test]
fn read_town_表記ゆれ_東京都千代田区丸の内() {
let city = generate_city_東京都千代田区();
let (rest, town) = read_town("丸ノ内一丁目9", city).unwrap();
assert_eq!(rest, "9");
assert_eq!(town, "丸の内一丁目");
}

#[test]
fn read_town_表記ゆれ_東京都千代田区一ツ橋() {
let city = generate_city_東京都千代田区();
let (rest, town) = read_town("一ッ橋二丁目1番", city).unwrap();
assert_eq!(rest, "1番");
assert_eq!(town, "一ツ橋二丁目");
}

fn generate_city_東京都千代田区() -> City {
City {
name: "千代田区".to_string(),
towns: vec![
Town::new("富士見一丁目", "", 35.697871, 139.746978),
Town::new("富士見二丁目", "", 35.698126, 139.743057),
Town::new("丸の内一丁目", "", 35.68156, 139.767201),
Town::new("一ツ橋一丁目", "", 35.691189, 139.757119),
Town::new("一ツ橋二丁目", "", 35.693171, 139.757346),
],
}
}

#[test]
fn read_town_表記ゆれ_京都府京都市左京区松ケ崎杉ケ海道町() {
let city = generate_city_京都府京都市左京区();
let (rest, town) = read_town("松ヶ崎杉ヶ海道町1", city).unwrap();
assert_eq!(rest, "1");
assert_eq!(town, "松ケ崎杉ケ海道町");
}

fn generate_city_京都府京都市左京区() -> City {
City {
name: "京都市左京区".to_string(),
towns: vec![
Town::new("松ケ崎杉ケ海道町", "", 35.047438, 135.779877),
Town::new("松ケ崎西池ノ内町", "", 35.054046, 135.773686),
Town::new("松ケ崎井出ケ鼻町", "", 35.056292, 135.790852),
],
}
}
}

0 comments on commit 3e94bb1

Please sign in to comment.