pub trait StringExt {
fn is_all_digits(&self) -> bool;
fn is_all_digits_unicode(&self) -> bool;
fn is_valid_integer(&self, allow_sign: bool) -> bool;
fn is_valid_float(&self, allow_sign: bool) -> bool;
fn to_i64(&self) -> Option<i64>;
fn to_f64(&self) -> Option<f64>;
}
impl<T: AsRef<str>> StringExt for T {
fn is_all_digits(&self) -> bool {
let s = self.as_ref();
if s.is_empty() {
return false;
}
s.chars().all(|c| c.is_ascii_digit())
}
fn is_all_digits_unicode(&self) -> bool {
let s = self.as_ref();
if s.is_empty() {
return false;
}
s.chars().all(|c| c.is_numeric())
}
fn is_valid_integer(&self, allow_sign: bool) -> bool {
let s = self.as_ref();
if s.is_empty() {
return false;
}
let mut chars = s.chars();
if allow_sign {
if let Some(first) = chars.next() {
if first == '+' || first == '-' {
if s.len() == 1 {
return false;
}
} else if !first.is_ascii_digit() {
return false;
}
}
}
chars.all(|c| c.is_ascii_digit())
}
fn is_valid_float(&self, allow_sign: bool) -> bool {
let s = self.as_ref();
if s.is_empty() {
return false;
}
let mut chars = s.chars().peekable();
let mut has_dot = false;
if allow_sign {
if let Some(&first) = chars.peek() {
if first == '+' || first == '-' {
chars.next();
if chars.peek().is_none() {
return false;
}
}
}
}
while let Some(c) = chars.next() {
if c == '.' {
if has_dot {
return false;
}
has_dot = true;
if chars.peek().is_none() {
return false; }
} else if !c.is_ascii_digit() {
return false;
}
}
true
}
fn to_i64(&self) -> Option<i64> {
let s = self.as_ref();
if self.is_valid_integer(true) {
s.parse().ok()
} else {
None
}
}
fn to_f64(&self) -> Option<f64> {
let s = self.as_ref();
if self.is_valid_float(true) {
s.parse().ok()
} else {
None
}
}
}
#[test]
fn test_string() {
use StringExt;
let test_strings = [
"12345",
"0",
"",
"123a45",
"12.34",
"-123",
"+456",
"3.14",
"-2.718",
];
println!("=== 字符串扩展方法示例 ===");
for s in &test_strings {
println!("字符串: '{}'", s);
println!(" is_all_digits(): {}", s.is_all_digits());
println!(" is_all_digits_unicode(): {}", s.is_all_digits_unicode());
println!(" is_valid_integer(true): {}", s.is_valid_integer(true));
println!(" is_valid_float(true): {}", s.is_valid_float(true));
println!(" to_i64(): {:?}", s.to_i64());
println!(" to_f64(): {:?}", s.to_f64());
println!();
}
println!("直接调用示例:");
println!("'12345'.is_all_digits() = {}", "12345".is_all_digits());
println!("'12.34'.to_f64() = {:?}", "12.34".to_f64());
let s = String::from("-42");
println!("String 类型调用: s.is_valid_integer(true) = {}", s.is_valid_integer(true));
println!("String 类型调用: s.to_i64() = {:?}", s.to_i64());
}
#[cfg(test)]
mod tests_string_ext {
use super::*;
#[test]
fn test_is_all_digits() {
assert!("12345".is_all_digits());
assert!(!"123a45".is_all_digits());
assert!(!"".is_all_digits());
}
#[test]
fn test_to_i64() {
assert_eq!("123".to_i64(), Some(123));
assert_eq!("-456".to_i64(), Some(-456));
assert_eq!("12.34".to_i64(), None);
assert_eq!("abc".to_i64(), None);
}
}