// 数据类型(标准 6.3.3.2.2 类型定义表)。
//
// 这一层只回答两个问题:这个类型字符串怎么解析、某个取值是否满足该类型。
// 它不认识具体字段名,也不绑定任何命令,方便上层用同一套规则校验数据区。
///|
/// 解析类型字符串,例如 `C4`、`N5`、`N14.2`、`YYYY`。
pub fn FieldType::parse(text : String) -> FieldType? {
let t = trim_text(text)
if t == "YYYY" {
return Some(Year)
}
if t == "MM" {
return Some(Month)
}
if t == "DD" {
return Some(Day)
}
if t == "hh" {
return Some(Hour)
}
if t == "mm" {
return Some(Minute)
}
if t == "ss" {
return Some(Second)
}
if t == "zzz" {
return Some(Millisecond)
}
let cs = chars_of(t)
if cs.length() < 2 {
return None
}
let head = cs[0]
if head == 'C' {
let n = parse_uint(cs, 1, cs.length() - 1)
match n {
Some(width) => if width > 0 { Some(Chars(width)) } else { None }
None => None
}
} else if head == 'N' {
// N5 或者 N14.2
let mut dot = -1
let mut i = 1
while i < cs.length() {
if cs[i] == '.' {
dot = i
break
} else if !is_digit_char(cs[i]) {
return None
}
i = i + 1
}
if dot < 0 {
match parse_uint(cs, 1, cs.length() - 1) {
Some(int_digits) =>
if int_digits > 0 {
Some(Number(int_digits, 0))
} else {
None
}
None => None
}
} else {
let int_part = parse_uint(cs, 1, dot - 1)
let frac_part = parse_uint(cs, dot + 1, cs.length() - dot - 1)
match (int_part, frac_part) {
(Some(id), Some(fd)) => if id > 0 { Some(Number(id, fd)) } else { None }
_ => None
}
}
} else {
None
}
}
///|
/// 把类型渲染回字符串形式。
pub fn FieldType::text(self : FieldType) -> String {
match self {
Chars(n) => "C\{n}"
Number(id, 0) => "N\{id}"
Number(id, fd) => "N\{id}.\{fd}"
Year => "YYYY"
Month => "MM"
Day => "DD"
Hour => "hh"
Minute => "mm"
Second => "ss"
Millisecond => "zzz"
}
}
///|
/// 校验取值是否满足类型;满足返回 `None`,否则返回原因。
pub fn FieldType::check(self : FieldType, value : String) -> String? {
let cs = chars_of(value)
let n = cs.length()
match self {
Chars(max) =>
if n <= max {
None
} else {
Some("最多 \{max} 个字符,实际 \{n} 个")
}
Number(int_digits, frac_digits) => {
let mut i = 0
if n > 0 && (cs[0] == '-' || cs[0] == '+') {
i = 1
}
let int_start = i
while i < n && is_digit_char(cs[i]) {
i = i + 1
}
let int_len = i - int_start
if int_len == 0 {
return Some("缺少整数部分")
}
if int_len > int_digits {
return Some(
"整数部分最多 \{int_digits} 位,实际 \{int_len} 位",
)
}
if i == n {
return None
}
if cs[i] != '.' {
return Some("出现非数字字符 \"\{cs[i]}\"")
}
let frac_start = i + 1
let mut j = frac_start
while j < n && is_digit_char(cs[j]) {
j = j + 1
}
let frac_len = j - frac_start
if frac_len > frac_digits {
return Some(
"小数部分最多 \{frac_digits} 位,实际 \{frac_len} 位",
)
}
if j != n {
return Some("小数部分出现非数字字符")
}
None
}
Year => check_digit_width(cs, 4, "年")
Month => check_digit_width(cs, 2, "月")
Day => check_digit_width(cs, 2, "日")
Hour => check_digit_width(cs, 2, "时")
Minute => check_digit_width(cs, 2, "分")
Second => check_digit_width(cs, 2, "秒")
Millisecond => check_digit_width(cs, 3, "毫秒")
}
}
///|
fn check_digit_width(
cs : Array[Char],
width : Int32,
label : String,
) -> String? {
if cs.length() != width {
return Some("\{label}应为 \{width} 位数字,实际 \{cs.length()} 位")
}
let mut i = 0
while i < cs.length() {
if !is_digit_char(cs[i]) {
return Some("\{label}必须是数字")
}
i = i + 1
}
None
}