From 8889dc37ab234707a9afe56d83cbc48b51a6beae Mon Sep 17 00:00:00 2001 From: Arata Date: Sat, 6 Apr 2024 01:47:49 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE?= =?UTF-8?q?=E6=A4=9C=E5=87=BA=E3=83=AD=E3=82=B8=E3=83=83=E3=82=AF=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 + Cargo.toml | 1 + crates/sos24-domain/Cargo.toml | 1 + crates/sos24-domain/src/entity/project.rs | 37 ++++++++++++++--------- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2112ca49..82037336 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2977,6 +2977,7 @@ dependencies = [ "percent-encoding", "regex", "thiserror", + "unicode-segmentation", "url", "uuid", ] diff --git a/Cargo.toml b/Cargo.toml index 90726e91..0eb888f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ tower = { version = "0.4.13", features = ["util"] } tower-http = { version = "0.5.1", features = ["cors", "trace"] } tracing = "0.1.40" tracing-subscriber = "0.3.18" +unicode-segmentation = "1.11.0" uuid = { version = "1.7.0", features = ["v4", "serde"] } url = "2.5.0" percent-encoding = "2.3.1" diff --git a/crates/sos24-domain/Cargo.toml b/crates/sos24-domain/Cargo.toml index 8aa2c127..b505f4f9 100644 --- a/crates/sos24-domain/Cargo.toml +++ b/crates/sos24-domain/Cargo.toml @@ -12,6 +12,7 @@ getset.workspace = true mockall.workspace = true regex.workspace = true thiserror.workspace = true +unicode-segmentation.workspace = true uuid.workspace = true url.workspace = true percent-encoding.workspace = true diff --git a/crates/sos24-domain/src/entity/project.rs b/crates/sos24-domain/src/entity/project.rs index 1e2c825c..c10adb76 100644 --- a/crates/sos24-domain/src/entity/project.rs +++ b/crates/sos24-domain/src/entity/project.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use bitflags::bitflags; use getset::Getters; use thiserror::Error; +use unicode_segmentation::UnicodeSegmentation; use crate::{ensure, impl_value_object}; @@ -280,7 +281,7 @@ impl BoundedString { #[derive(Debug, Error)] pub enum BoundedStringError { #[error("Invalid character: `{0}`")] - InvalidCharacter(char), + InvalidCharacter(String), #[error("Empty string is not allowed")] Empty, #[error("Too long (max: {0})")] @@ -293,21 +294,28 @@ impl TryFrom for BoundedString { fn try_from(value: String) -> Result { let mut length = 0; // 文字列長を3倍してカウントする - for c in value.chars() { - if emojis::get(&c.to_string()).is_some() { - return Err(BoundedStringError::InvalidCharacter(c)); + let is_small = |c: char| match c { + '\u{0021}'..='\u{007E}' // 半角英数字・記号 + | '\u{FF10}'..='\u{FF19}' // 全角数字 + | '\u{FF21}'..='\u{FF3A}' // 全角英語(大文字) + | '\u{FF41}'..='\u{FF5A}' // 全角英語(小文字) + => true, + _ => false, + }; + + for grapheme_cluster in value.graphemes(true) { + if emojis::get(grapheme_cluster).is_some() { + return Err(BoundedStringError::InvalidCharacter( + grapheme_cluster.to_string(), + )); } - let char_length = match c { - '\u{0021}'..='\u{007E}' // 半角英数字・記号 - | '\u{FF10}'..='\u{FF19}' // 全角数字 - | '\u{FF21}'..='\u{FF3A}' // 全角英語(大文字) - | '\u{FF41}'..='\u{FF5A}' // 全角英語(小文字) - => 2, - _ => 3, - }; - - length += char_length; + let mut chars = grapheme_cluster.chars(); + let is_small_char = chars + .next() + .map(|c| is_small(c) && chars.next().is_none()) + .unwrap_or(false); + length += if is_small_char { 2 } else { 3 }; } if length == 0 { @@ -422,5 +430,6 @@ mod tests { assert!(ProjectTitle::try_from(format!("{kana18}AAAA")).is_err()); assert!(ProjectTitle::try_from("🙂".to_string()).is_err()); + assert!(ProjectTitle::try_from("企画名#️⃣appare".to_string()).is_err()); } }