Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions stationapi/src/domain/ipa.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
/// Common katakana suffixes for line names, ordered longest-first for greedy matching.
const LINE_NAME_SUFFIXES: &[&str] = &["ホンセン", "シセン", "セン"];
/// Suffixes that should NOT be stripped even though they end with セン.
const LINE_NAME_SUFFIX_EXCEPTIONS: &[&str] = &["シンカンセン"];

/// Strip a common line-name suffix (線/本線/支線) from a katakana string.
/// 新幹線 (Shinkansen) is preserved as it is used as-is in English.
/// Returns the stem (without the suffix). If no known suffix is found, returns the input unchanged.
pub fn strip_line_name_suffix(input: &str) -> &str {
for exception in LINE_NAME_SUFFIX_EXCEPTIONS {
if input.ends_with(exception) {
return input;
}
}
for suffix in LINE_NAME_SUFFIXES {
if let Some(stem) = input.strip_suffix(suffix) {
if !stem.is_empty() {
return stem;
}
}
}
input
}

/// Convert a katakana string to its IPA transcription.
/// Returns `None` if the input contains characters that cannot be converted.
pub fn katakana_to_ipa(input: &str) -> Option<String> {
Expand Down Expand Up @@ -608,4 +632,56 @@ mod tests {
"dokkʲoːdaiɡakɯmae soːkamat͡sɯbaɾa"
);
}

// ============================================
// strip_line_name_suffix tests
// ============================================

#[test]
fn test_strip_sen() {
assert_eq!(
strip_line_name_suffix("セイブイケブクロセン"),
"セイブイケブクロ"
);
}

#[test]
fn test_strip_honsen() {
assert_eq!(
strip_line_name_suffix("トウカイドウホンセン"),
"トウカイドウ"
);
}

#[test]
fn test_strip_shinkansen_preserved() {
// 新幹線(Shinkansen)は英語でもそのまま使われるので除去しない
assert_eq!(
strip_line_name_suffix("トウホクシンカンセン"),
"トウホクシンカンセン"
);
}

#[test]
fn test_strip_shisen() {
assert_eq!(
strip_line_name_suffix("ナガノハラクサツグチシセン"),
"ナガノハラクサツグチ"
);
}

#[test]
fn test_strip_no_suffix() {
// ライン等セン以外の末尾はそのまま返す
assert_eq!(
strip_line_name_suffix("ショウナンシンジュクライン"),
"ショウナンシンジュクライン"
);
}

#[test]
fn test_strip_bare_sen_returns_unchanged() {
// "セン" だけの場合、stemが空になるので除去しない
assert_eq!(strip_line_name_suffix("セン"), "セン");
}
}
5 changes: 3 additions & 2 deletions stationapi/src/use_case/dto/line.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::{
domain::{
entity::{gtfs::TransportType, line::Line},
ipa::katakana_to_ipa,
ipa::{katakana_to_ipa, strip_line_name_suffix},
},
proto::{Line as GrpcLine, TransportType as GrpcTransportType},
};

impl From<Line> for GrpcLine {
fn from(line: Line) -> Self {
let name_ipa = katakana_to_ipa(&line.line_name_k).filter(|ipa| !ipa.is_empty());
let name_ipa = katakana_to_ipa(strip_line_name_suffix(&line.line_name_k))
.filter(|ipa| !ipa.is_empty());
// バス路線の場合は line_type を OtherLineType (0) に強制
// (鉄道用の line_type が誤って設定されている可能性があるため)
let line_type = if line.transport_type == TransportType::Bus {
Expand Down